mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-24 15:09:39 -05:00
Constellation Operator: Add image version field (#649)
This commit is contained in:
parent
89b25f8ebb
commit
1af3ff00ad
@ -36,6 +36,10 @@ spec:
|
||||
image:
|
||||
description: ImageReference is the image to use for all nodes.
|
||||
type: string
|
||||
imageVersion:
|
||||
description: ImageVersion is the CSP independent version of the image
|
||||
to use for all nodes.
|
||||
type: string
|
||||
type: object
|
||||
status:
|
||||
description: NodeImageStatus defines the observed state of NodeImage.
|
||||
|
@ -79,6 +79,10 @@ spec:
|
||||
volumeMounts:
|
||||
- mountPath: /etc/kubernetes/pki/etcd
|
||||
name: etcd-certs
|
||||
- mountPath: /host/usr/lib/os-release
|
||||
name: usr-lib-os-release
|
||||
- mountPath: /etc/os-release
|
||||
name: etc-os-release
|
||||
- mountPath: /etc/azure
|
||||
name: azureconfig
|
||||
readOnly: true
|
||||
@ -103,6 +107,16 @@ spec:
|
||||
path: /etc/kubernetes/pki/etcd
|
||||
type: Directory
|
||||
name: etcd-certs
|
||||
- hostPath:
|
||||
path: /usr/lib/os-release
|
||||
type: File
|
||||
optional: true
|
||||
name: usr-lib-os-release
|
||||
- hostPath:
|
||||
path: /etc/os-release
|
||||
type: File
|
||||
optional: true
|
||||
name: etc-os-release
|
||||
- name: azureconfig
|
||||
secret:
|
||||
optional: true
|
||||
|
@ -97,6 +97,10 @@ spec:
|
||||
volumeMounts:
|
||||
- mountPath: /etc/kubernetes/pki/etcd
|
||||
name: etcd-certs
|
||||
- mountPath: /host/usr/lib/os-release
|
||||
name: usr-lib-os-release
|
||||
- mountPath: /etc/os-release
|
||||
name: etc-os-release
|
||||
- mountPath: /etc/azure
|
||||
name: azureconfig
|
||||
readOnly: true
|
||||
@ -121,6 +125,16 @@ spec:
|
||||
path: /etc/kubernetes/pki/etcd
|
||||
type: Directory
|
||||
name: etcd-certs
|
||||
- hostPath:
|
||||
path: /usr/lib/os-release
|
||||
type: File
|
||||
optional: true
|
||||
name: usr-lib-os-release
|
||||
- hostPath:
|
||||
path: /etc/os-release
|
||||
type: File
|
||||
optional: true
|
||||
name: etc-os-release
|
||||
- name: azureconfig
|
||||
secret:
|
||||
optional: true
|
||||
|
@ -97,6 +97,10 @@ spec:
|
||||
volumeMounts:
|
||||
- mountPath: /etc/kubernetes/pki/etcd
|
||||
name: etcd-certs
|
||||
- mountPath: /host/usr/lib/os-release
|
||||
name: usr-lib-os-release
|
||||
- mountPath: /etc/os-release
|
||||
name: etc-os-release
|
||||
- mountPath: /etc/azure
|
||||
name: azureconfig
|
||||
readOnly: true
|
||||
@ -121,6 +125,16 @@ spec:
|
||||
path: /etc/kubernetes/pki/etcd
|
||||
type: Directory
|
||||
name: etcd-certs
|
||||
- hostPath:
|
||||
path: /usr/lib/os-release
|
||||
type: File
|
||||
optional: true
|
||||
name: usr-lib-os-release
|
||||
- hostPath:
|
||||
path: /etc/os-release
|
||||
type: File
|
||||
optional: true
|
||||
name: etc-os-release
|
||||
- name: azureconfig
|
||||
secret:
|
||||
optional: true
|
||||
|
@ -97,6 +97,10 @@ spec:
|
||||
volumeMounts:
|
||||
- mountPath: /etc/kubernetes/pki/etcd
|
||||
name: etcd-certs
|
||||
- mountPath: /host/usr/lib/os-release
|
||||
name: usr-lib-os-release
|
||||
- mountPath: /etc/os-release
|
||||
name: etc-os-release
|
||||
- mountPath: /etc/azure
|
||||
name: azureconfig
|
||||
readOnly: true
|
||||
@ -121,6 +125,16 @@ spec:
|
||||
path: /etc/kubernetes/pki/etcd
|
||||
type: Directory
|
||||
name: etcd-certs
|
||||
- hostPath:
|
||||
path: /usr/lib/os-release
|
||||
type: File
|
||||
optional: true
|
||||
name: usr-lib-os-release
|
||||
- hostPath:
|
||||
path: /etc/os-release
|
||||
type: File
|
||||
optional: true
|
||||
name: etc-os-release
|
||||
- name: azureconfig
|
||||
secret:
|
||||
optional: true
|
||||
|
@ -15,6 +15,8 @@ import (
|
||||
type NodeImageSpec struct {
|
||||
// ImageReference is the image to use for all nodes.
|
||||
ImageReference string `json:"image,omitempty"`
|
||||
// ImageVersion is the CSP independent version of the image to use for all nodes.
|
||||
ImageVersion string `json:"imageVersion,omitempty"`
|
||||
}
|
||||
|
||||
// NodeImageStatus defines the observed state of NodeImage.
|
||||
|
@ -38,6 +38,10 @@ spec:
|
||||
image:
|
||||
description: ImageReference is the image to use for all nodes.
|
||||
type: string
|
||||
imageVersion:
|
||||
description: ImageVersion is the CSP independent version of the image
|
||||
to use for all nodes.
|
||||
type: string
|
||||
type: object
|
||||
status:
|
||||
description: NodeImageStatus defines the observed state of NodeImage.
|
||||
|
@ -50,6 +50,10 @@ spec:
|
||||
volumeMounts:
|
||||
- mountPath: /etc/kubernetes/pki/etcd
|
||||
name: etcd-certs
|
||||
- mountPath: /host/usr/lib/os-release
|
||||
name: usr-lib-os-release
|
||||
- mountPath: /etc/os-release
|
||||
name: etc-os-release
|
||||
- mountPath: /etc/azure
|
||||
name: azureconfig
|
||||
readOnly: true
|
||||
@ -68,6 +72,16 @@ spec:
|
||||
hostPath:
|
||||
path: /etc/kubernetes/pki/etcd
|
||||
type: Directory
|
||||
- hostPath:
|
||||
path: /usr/lib/os-release
|
||||
type: File
|
||||
optional: true
|
||||
name: usr-lib-os-release
|
||||
- hostPath:
|
||||
path: /etc/os-release
|
||||
type: File
|
||||
optional: true
|
||||
name: etc-os-release
|
||||
- name: azureconfig
|
||||
secret:
|
||||
secretName: azureconfig
|
||||
|
@ -18,10 +18,12 @@ import (
|
||||
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
)
|
||||
|
||||
// InitialResources creates the initial resources for the node operator.
|
||||
func InitialResources(ctx context.Context, k8sClient client.Writer, scalingGroupGetter scalingGroupGetter, uid string) error {
|
||||
func InitialResources(ctx context.Context, k8sClient client.Writer, imageInfo imageInfoGetter, scalingGroupGetter scalingGroupGetter, uid string) error {
|
||||
logr := log.FromContext(ctx)
|
||||
controlPlaneGroupIDs, workerGroupIDs, err := scalingGroupGetter.ListScalingGroups(ctx, uid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("listing scaling groups: %w", err)
|
||||
@ -40,7 +42,15 @@ func InitialResources(ctx context.Context, k8sClient client.Writer, scalingGroup
|
||||
if err != nil {
|
||||
return fmt.Errorf("determining initial node image: %w", err)
|
||||
}
|
||||
if err := createNodeImage(ctx, k8sClient, imageReference); err != nil {
|
||||
imageVersion, err := imageInfo.ImageVersion(imageReference)
|
||||
if err != nil {
|
||||
// do not fail if the image version cannot be determined
|
||||
// this is important for backwards compatibility
|
||||
logr.Error(err, "determining initial node image version")
|
||||
imageVersion = ""
|
||||
}
|
||||
|
||||
if err := createNodeImage(ctx, k8sClient, imageReference, imageVersion); err != nil {
|
||||
return fmt.Errorf("creating initial node image %q: %w", imageReference, err)
|
||||
}
|
||||
for _, groupID := range controlPlaneGroupIDs {
|
||||
@ -101,7 +111,7 @@ func createAutoscalingStrategy(ctx context.Context, k8sClient client.Writer, pro
|
||||
}
|
||||
|
||||
// createNodeImage creates the initial nodeimage resource if it does not exist yet.
|
||||
func createNodeImage(ctx context.Context, k8sClient client.Writer, imageReference string) error {
|
||||
func createNodeImage(ctx context.Context, k8sClient client.Writer, imageReference, imageVersion string) error {
|
||||
err := k8sClient.Create(ctx, &updatev1alpha1.NodeImage{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: "update.edgeless.systems/v1alpha1", Kind: "NodeImage"},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@ -109,6 +119,7 @@ func createNodeImage(ctx context.Context, k8sClient client.Writer, imageReferenc
|
||||
},
|
||||
Spec: updatev1alpha1.NodeImageSpec{
|
||||
ImageReference: imageReference,
|
||||
ImageVersion: imageVersion,
|
||||
},
|
||||
})
|
||||
if k8sErrors.IsAlreadyExists(err) {
|
||||
@ -139,6 +150,10 @@ func createScalingGroup(ctx context.Context, config newScalingGroupConfig) error
|
||||
return err
|
||||
}
|
||||
|
||||
type imageInfoGetter interface {
|
||||
ImageVersion(imageReference string) (string, error)
|
||||
}
|
||||
|
||||
type scalingGroupGetter interface {
|
||||
// GetScalingGroupImage retrieves the image currently used by a scaling group.
|
||||
GetScalingGroupImage(ctx context.Context, scalingGroupID string) (string, error)
|
||||
|
@ -87,7 +87,7 @@ func TestInitialResources(t *testing.T) {
|
||||
|
||||
k8sClient := &stubK8sClient{createErr: tc.createErr}
|
||||
scalingGroupGetter := newScalingGroupGetter(tc.items, tc.imageErr, tc.nameErr, tc.listErr)
|
||||
err := InitialResources(context.Background(), k8sClient, scalingGroupGetter, "uid")
|
||||
err := InitialResources(context.Background(), k8sClient, &stubImageInfo{}, scalingGroupGetter, "uid")
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
@ -183,6 +183,7 @@ func TestCreateNodeImage(t *testing.T) {
|
||||
},
|
||||
Spec: updatev1alpha1.NodeImageSpec{
|
||||
ImageReference: "image-reference",
|
||||
ImageVersion: "image-version",
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -199,6 +200,7 @@ func TestCreateNodeImage(t *testing.T) {
|
||||
},
|
||||
Spec: updatev1alpha1.NodeImageSpec{
|
||||
ImageReference: "image-reference",
|
||||
ImageVersion: "image-version",
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -210,7 +212,7 @@ func TestCreateNodeImage(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
k8sClient := &stubK8sClient{createErr: tc.createErr}
|
||||
err := createNodeImage(context.Background(), k8sClient, "image-reference")
|
||||
err := createNodeImage(context.Background(), k8sClient, "image-reference", "image-version")
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
@ -297,6 +299,15 @@ func (s *stubK8sClient) Create(ctx context.Context, obj client.Object, opts ...c
|
||||
return s.createErr
|
||||
}
|
||||
|
||||
type stubImageInfo struct {
|
||||
imageVersion string
|
||||
err error
|
||||
}
|
||||
|
||||
func (s stubImageInfo) ImageVersion(_ string) (string, error) {
|
||||
return s.imageVersion, s.err
|
||||
}
|
||||
|
||||
type stubScalingGroupGetter struct {
|
||||
store map[string]scalingGroupStoreItem
|
||||
imageErr error
|
||||
|
@ -0,0 +1,154 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package deploy
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
// ImageInfo retrieves OS image information.
|
||||
type ImageInfo struct {
|
||||
fs *afero.Afero
|
||||
}
|
||||
|
||||
// NewImageInfo creates a new imageInfo.
|
||||
func NewImageInfo() *ImageInfo {
|
||||
return &ImageInfo{
|
||||
fs: &afero.Afero{Fs: afero.NewOsFs()},
|
||||
}
|
||||
}
|
||||
|
||||
// ImageVersion tries to parse the image version from the host mounted os-release file.
|
||||
// If the file is not present or does not contain the version, a fallback lookup is performed.
|
||||
func (i *ImageInfo) ImageVersion(imageReference string) (string, error) {
|
||||
var version string
|
||||
var err error
|
||||
for _, path := range osReleasePaths {
|
||||
version, err = i.getOSReleaseImageVersion(path)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if version != "" {
|
||||
return version, nil
|
||||
}
|
||||
return imageVersionFromFallback(imageReference)
|
||||
}
|
||||
|
||||
// getOSReleaseImageVersion reads the os-release file and returns the image version (if present).
|
||||
func (i *ImageInfo) getOSReleaseImageVersion(path string) (string, error) {
|
||||
osRelease, err := i.fs.Open(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer osRelease.Close()
|
||||
osReleaseMap, err := parseOSRelease(bufio.NewScanner(osRelease))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
version, ok := osReleaseMap[versionKey]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("IMAGE_VERSION not found in %s", path)
|
||||
}
|
||||
return version, nil
|
||||
}
|
||||
|
||||
// parseOSRelease parses the os-release file and returns a map of key-value pairs.
|
||||
// The os-release file is a simple key-value file.
|
||||
// The format is specified in https://www.freedesktop.org/software/systemd/man/os-release.html.
|
||||
func parseOSRelease(osRelease *bufio.Scanner) (map[string]string, error) {
|
||||
osReleaseMap := make(map[string]string)
|
||||
for osRelease.Scan() {
|
||||
line := osRelease.Text()
|
||||
matches := osReleaseLine.FindStringSubmatch(line)
|
||||
if len(matches) < 6 {
|
||||
continue
|
||||
}
|
||||
key := matches[1]
|
||||
var value string
|
||||
// group 3 is the value with double quotes
|
||||
// group 4 is the value with single quotes
|
||||
// group 5 is the value without quotes
|
||||
for i := 3; i < 6; i++ {
|
||||
if matches[i] != "" {
|
||||
value = matches[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
// unescape the following characters: \\, \$, \", \', \`
|
||||
value = osReleaseUnescape.ReplaceAllString(value, "$1")
|
||||
osReleaseMap[key] = value
|
||||
}
|
||||
if err := osRelease.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return osReleaseMap, nil
|
||||
}
|
||||
|
||||
// imageVersionFromFallback tries to guess the image version from the image reference.
|
||||
// It is a fallback mechanism in case the os-release file is not present or does not contain the version.
|
||||
// This was the case for older images (< v2.3.0).
|
||||
func imageVersionFromFallback(imageReference string) (string, error) {
|
||||
version, ok := fallbackLookup[strings.ToLower(imageReference)]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("image version not found in fallback lookup")
|
||||
}
|
||||
return version, nil
|
||||
}
|
||||
|
||||
const versionKey = "IMAGE_VERSION"
|
||||
|
||||
var (
|
||||
osReleaseLine = regexp.MustCompile(`^(?P<name>[a-zA-Z0-9_]+)=("(?P<v1>.*)"|'(?P<v2>.*)'|(?P<v3>[^\n"']+))$`)
|
||||
osReleaseUnescape = regexp.MustCompile(`\\([\\\$\"\'` + "`" + `])`)
|
||||
osReleasePaths = []string{
|
||||
"/host/etc/os-release",
|
||||
"/host/usr/lib/os-release",
|
||||
}
|
||||
|
||||
fallbackLookup = map[string]string{
|
||||
// AWS
|
||||
"ami-06b8cbf4837a0a57c": "v2.2.2",
|
||||
"ami-02e96dc04a9e438cd": "v2.2.2",
|
||||
"ami-028ead928a9034b2f": "v2.2.2",
|
||||
"ami-032ac10dd8d8266e3": "v2.2.1",
|
||||
"ami-032e0d57cc4395088": "v2.2.1",
|
||||
"ami-053c3e49e19b96bdd": "v2.2.1",
|
||||
"ami-0e27ebcefc38f648b": "v2.2.0",
|
||||
"ami-098cd37f66523b7c3": "v2.2.0",
|
||||
"ami-04a87d302e2509aad": "v2.2.0",
|
||||
|
||||
// Azure
|
||||
"/communitygalleries/constellationcvm-b3782fa0-0df7-4f2f-963e-fc7fc42663df/images/constellation/versions/2.2.2": "v2.2.2",
|
||||
"/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourcegroups/constellation-images/providers/microsoft.compute/galleries/constellation/images/constellation/versions/2.2.2": "v2.2.2",
|
||||
"/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourcegroups/constellation-images/providers/microsoft.compute/galleries/constellation_cvm/images/constellation/versions/2.2.2": "v2.2.2",
|
||||
"/communitygalleries/constellationcvm-b3782fa0-0df7-4f2f-963e-fc7fc42663df/images/constellation/versions/2.2.1": "v2.2.1",
|
||||
"/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourcegroups/constellation-images/providers/microsoft.compute/galleries/constellation/images/constellation/versions/2.2.1": "v2.2.1",
|
||||
"/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourcegroups/constellation-images/providers/microsoft.compute/galleries/constellation_cvm/images/constellation/versions/2.2.1": "v2.2.1",
|
||||
"/communitygalleries/constellationcvm-b3782fa0-0df7-4f2f-963e-fc7fc42663df/images/constellation/versions/2.2.0": "v2.2.0",
|
||||
"/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourcegroups/constellation-images/providers/microsoft.compute/galleries/constellation/images/constellation/versions/2.2.0": "v2.2.0",
|
||||
"/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourcegroups/constellation-images/providers/microsoft.compute/galleries/constellation_cvm/images/constellation/versions/2.2.0": "v2.2.0",
|
||||
"/communitygalleries/constellationcvm-b3782fa0-0df7-4f2f-963e-fc7fc42663df/images/constellation/versions/2.1.0": "v2.1.0",
|
||||
"/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourcegroups/constellation-images/providers/microsoft.compute/galleries/constellation/images/constellation/versions/2.1.0": "v2.1.0",
|
||||
"/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourcegroups/constellation-images/providers/microsoft.compute/galleries/constellation_cvm/images/constellation/versions/2.1.0": "v2.1.0",
|
||||
"/communitygalleries/constellationcvm-b3782fa0-0df7-4f2f-963e-fc7fc42663df/images/constellation/versions/2.0.0": "v2.0.0",
|
||||
"/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourcegroups/constellation-images/providers/microsoft.compute/galleries/constellation/images/constellation/versions/2.0.0": "v2.0.0",
|
||||
"/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourcegroups/constellation-images/providers/microsoft.compute/galleries/constellation_cvm/images/constellation/versions/2.0.0": "v2.0.0",
|
||||
|
||||
// GCP
|
||||
"projects/constellation-images/global/images/constellation-v2-2-2": "v2.2.2",
|
||||
"projects/constellation-images/global/images/constellation-v2-2-1": "v2.2.1",
|
||||
"projects/constellation-images/global/images/constellation-v2-2-0": "v2.2.0",
|
||||
"projects/constellation-images/global/images/constellation-v2-1-0": "v2.1.0",
|
||||
"projects/constellation-images/global/images/constellation-v2-0-0": "v2.0.0",
|
||||
}
|
||||
)
|
@ -0,0 +1,229 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package deploy
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestImageVersion(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
imageReference string
|
||||
createFile [2]string
|
||||
wantVersion string
|
||||
wantErr bool
|
||||
}{
|
||||
"version found in /etc": {
|
||||
imageReference: "some-reference",
|
||||
createFile: [2]string{"/host/etc/os-release", osRelease},
|
||||
wantVersion: "v2.3.0",
|
||||
},
|
||||
"version found in /usr/lib": {
|
||||
imageReference: "some-reference",
|
||||
createFile: [2]string{"/host/usr/lib/os-release", osRelease},
|
||||
wantVersion: "v2.3.0",
|
||||
},
|
||||
"version not found": {
|
||||
imageReference: "some-reference",
|
||||
wantErr: true,
|
||||
},
|
||||
"fallback version found": {
|
||||
imageReference: "ami-04a87d302e2509aad",
|
||||
wantVersion: "v2.2.0",
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
fs := afero.NewMemMapFs()
|
||||
if tc.createFile[0] != "" {
|
||||
err := afero.WriteFile(fs, tc.createFile[0], []byte(tc.createFile[1]), os.ModePerm)
|
||||
require.NoError(err)
|
||||
}
|
||||
|
||||
imageInfo := &ImageInfo{
|
||||
fs: &afero.Afero{Fs: fs},
|
||||
}
|
||||
|
||||
version, err := imageInfo.ImageVersion(tc.imageReference)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tc.wantVersion, version)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOSReleaseImageVersion(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
path string
|
||||
wantVersion string
|
||||
wantErr bool
|
||||
}{
|
||||
"version found": {
|
||||
path: "os-release",
|
||||
wantVersion: "v2.3.0",
|
||||
},
|
||||
"invalid path": {
|
||||
path: "not/a/real/path",
|
||||
wantErr: true,
|
||||
},
|
||||
"empty file": {
|
||||
path: "empty",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
fs := afero.NewMemMapFs()
|
||||
err := afero.WriteFile(fs, "os-release", []byte(osRelease), os.ModePerm)
|
||||
require.NoError(err)
|
||||
err = afero.WriteFile(fs, "empty", []byte{}, os.ModePerm)
|
||||
require.NoError(err)
|
||||
|
||||
imageInfo := &ImageInfo{
|
||||
fs: &afero.Afero{Fs: fs},
|
||||
}
|
||||
|
||||
version, err := imageInfo.getOSReleaseImageVersion(tc.path)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tc.wantVersion, version)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseOSRelease(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
osReleaseMap, err := parseOSRelease(bufio.NewScanner(strings.NewReader(osRelease)))
|
||||
require.NoError(err)
|
||||
assert.Equal(wantMap, osReleaseMap)
|
||||
}
|
||||
|
||||
func TestImageVersionFromFallback(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
imageReference string
|
||||
wantVersion string
|
||||
wantErr bool
|
||||
}{
|
||||
"AWS reference": {
|
||||
imageReference: "ami-06b8cbf4837a0a57c",
|
||||
wantVersion: "v2.2.2",
|
||||
},
|
||||
"Azure reference": {
|
||||
imageReference: "/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourceGroups/constellation-images/providers/Microsoft.Compute/galleries/Constellation/images/constellation/versions/2.1.0",
|
||||
wantVersion: "v2.1.0",
|
||||
},
|
||||
"GCP reference": {
|
||||
imageReference: "projects/constellation-images/global/images/constellation-v2-0-0",
|
||||
wantVersion: "v2.0.0",
|
||||
},
|
||||
"unknown reference": {
|
||||
imageReference: "unknown",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
version, err := imageVersionFromFallback(tc.imageReference)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tc.wantVersion, version)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const osRelease = `
|
||||
# Some comment
|
||||
# Some empty lines below
|
||||
|
||||
|
||||
SINGLE_QUOTED_VALUE='WOW! This is a single quoted value!'
|
||||
DOUBLE_QUOTED_VALUE="WOW! This is a double quoted value!"
|
||||
ESCAPED_BACKSLASH='This is a string with an escaped backslash: \\'
|
||||
ESCAPED_DOLLAR='This is a string with an escaped dollar: \$'
|
||||
ESCAPED_DOUBLE_QUOTE='This is a string with an escaped double quote: \"'
|
||||
ESCAPED_SINGLE_QUOTE="This is a string with an escaped single quote: \'"
|
||||
NAME="Fedora Linux"
|
||||
VERSION="37 (Thirty Seven)"
|
||||
ID=fedora
|
||||
PRETTY_NAME="Fedora Linux 37 (Thirty Seven)"
|
||||
ANSI_COLOR="0;38;2;60;110;180"
|
||||
VERSION_ID=37
|
||||
VERSION_CODENAME=""
|
||||
PLATFORM_ID="platform:f37"
|
||||
LOGO=fedora-logo-icon
|
||||
CPE_NAME="cpe:/o:fedoraproject:fedora:37"
|
||||
DEFAULT_HOSTNAME="fedora"
|
||||
HOME_URL="https://fedoraproject.org/"
|
||||
DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f37/system-administrators-guide/"
|
||||
SUPPORT_URL="https://ask.fedoraproject.org/"
|
||||
BUG_REPORT_URL="https://bugzilla.redhat.com/"
|
||||
REDHAT_BUGZILLA_PRODUCT="Fedora"
|
||||
REDHAT_BUGZILLA_PRODUCT_VERSION=37
|
||||
REDHAT_SUPPORT_PRODUCT="Fedora"
|
||||
REDHAT_SUPPORT_PRODUCT_VERSION=37
|
||||
IMAGE_ID="constellation"
|
||||
IMAGE_VERSION="v2.3.0"
|
||||
`
|
||||
|
||||
var wantMap = map[string]string{
|
||||
"NAME": `Fedora Linux`,
|
||||
"VERSION": `37 (Thirty Seven)`,
|
||||
"ID": `fedora`,
|
||||
"SINGLE_QUOTED_VALUE": `WOW! This is a single quoted value!`,
|
||||
"DOUBLE_QUOTED_VALUE": `WOW! This is a double quoted value!`,
|
||||
"ESCAPED_BACKSLASH": `This is a string with an escaped backslash: \`,
|
||||
"ESCAPED_DOLLAR": `This is a string with an escaped dollar: $`,
|
||||
"ESCAPED_DOUBLE_QUOTE": `This is a string with an escaped double quote: "`,
|
||||
"ESCAPED_SINGLE_QUOTE": `This is a string with an escaped single quote: '`,
|
||||
"VERSION_ID": `37`,
|
||||
"VERSION_CODENAME": ``,
|
||||
"PLATFORM_ID": `platform:f37`,
|
||||
"PRETTY_NAME": `Fedora Linux 37 (Thirty Seven)`,
|
||||
"ANSI_COLOR": `0;38;2;60;110;180`,
|
||||
"LOGO": `fedora-logo-icon`,
|
||||
"CPE_NAME": `cpe:/o:fedoraproject:fedora:37`,
|
||||
"DEFAULT_HOSTNAME": `fedora`,
|
||||
"HOME_URL": `https://fedoraproject.org/`,
|
||||
"DOCUMENTATION_URL": `https://docs.fedoraproject.org/en-US/fedora/f37/system-administrators-guide/`,
|
||||
"SUPPORT_URL": `https://ask.fedoraproject.org/`,
|
||||
"BUG_REPORT_URL": `https://bugzilla.redhat.com/`,
|
||||
"REDHAT_BUGZILLA_PRODUCT": `Fedora`,
|
||||
"REDHAT_BUGZILLA_PRODUCT_VERSION": `37`,
|
||||
"REDHAT_SUPPORT_PRODUCT": `Fedora`,
|
||||
"REDHAT_SUPPORT_PRODUCT_VERSION": `37`,
|
||||
"IMAGE_ID": `constellation`,
|
||||
"IMAGE_VERSION": `v2.3.0`,
|
||||
}
|
@ -126,8 +126,8 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
defer etcdClient.Close()
|
||||
|
||||
if err := deploy.InitialResources(context.Background(), k8sClient, cspClient, os.Getenv(constellationUID)); err != nil {
|
||||
imageInfo := deploy.NewImageInfo()
|
||||
if err := deploy.InitialResources(context.Background(), k8sClient, imageInfo, cspClient, os.Getenv(constellationUID)); err != nil {
|
||||
setupLog.Error(err, "Unable to deploy initial resources")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user