constellation/cli/internal/cloudcmd/upgrade_test.go
Daniel Weiße 67d0424f0e
AB#2639 Add functions to fetch k8s and helm version of Constellation (#637)
Signed-off-by: Daniel Weiße <dw@edgeless.systems>
2022-11-24 16:39:33 +01:00

274 lines
6.7 KiB
Go

/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package cloudcmd
import (
"bytes"
"context"
"encoding/json"
"errors"
"testing"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
func TestUpdateMeasurements(t *testing.T) {
someErr := errors.New("error")
testCases := map[string]struct {
updater *stubClientInterface
newMeasurements measurements.M
wantUpdate bool
wantErr bool
}{
"success": {
updater: &stubClientInterface{
oldMeasurements: &corev1.ConfigMap{
Data: map[string]string{
constants.MeasurementsFilename: `{"0":{"expected":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","warnOnly":false}}`,
},
},
},
newMeasurements: measurements.M{
0: measurements.WithAllBytes(0xBB, false),
},
wantUpdate: true,
},
"measurements are the same": {
updater: &stubClientInterface{
oldMeasurements: &corev1.ConfigMap{
Data: map[string]string{
constants.MeasurementsFilename: `{"0":{"expected":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","warnOnly":false}}`,
},
},
},
newMeasurements: measurements.M{
0: measurements.WithAllBytes(0xAA, false),
},
},
"trying to set warnOnly to true results in error": {
updater: &stubClientInterface{
oldMeasurements: &corev1.ConfigMap{
Data: map[string]string{
constants.MeasurementsFilename: `{"0":{"expected":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","warnOnly":false}}`,
},
},
},
newMeasurements: measurements.M{
0: measurements.WithAllBytes(0xAA, true),
},
wantErr: true,
},
"setting warnOnly to false is allowed": {
updater: &stubClientInterface{
oldMeasurements: &corev1.ConfigMap{
Data: map[string]string{
constants.MeasurementsFilename: `{"0":{"expected":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","warnOnly":true}}`,
},
},
},
newMeasurements: measurements.M{
0: measurements.WithAllBytes(0xAA, false),
},
wantUpdate: true,
},
"getCurrent error": {
updater: &stubClientInterface{getErr: someErr},
wantErr: true,
},
"update error": {
updater: &stubClientInterface{
oldMeasurements: &corev1.ConfigMap{
Data: map[string]string{
constants.MeasurementsFilename: `{"0":{"expected":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","warnOnly":false}}`,
},
},
updateErr: someErr,
},
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
upgrader := &Upgrader{
stableInterface: tc.updater,
outWriter: &bytes.Buffer{},
}
err := upgrader.updateMeasurements(context.Background(), tc.newMeasurements)
if tc.wantErr {
assert.Error(err)
return
}
assert.NoError(err)
if tc.wantUpdate {
newMeasurementsJSON, err := json.Marshal(tc.newMeasurements)
require.NoError(t, err)
assert.JSONEq(string(newMeasurementsJSON), tc.updater.updatedMeasurements.Data[constants.MeasurementsFilename])
} else {
assert.Nil(tc.updater.updatedMeasurements)
}
})
}
}
type stubClientInterface struct {
oldMeasurements *corev1.ConfigMap
updatedMeasurements *corev1.ConfigMap
k8sVersion string
getErr error
updateErr error
k8sVersionErr error
}
func (u *stubClientInterface) getCurrent(context.Context, string) (*corev1.ConfigMap, error) {
return u.oldMeasurements, u.getErr
}
func (u *stubClientInterface) update(_ context.Context, updatedMeasurements *corev1.ConfigMap) (*corev1.ConfigMap, error) {
u.updatedMeasurements = updatedMeasurements
return nil, u.updateErr
}
func (u *stubClientInterface) kubernetesVersion() (string, error) {
return u.k8sVersion, u.k8sVersionErr
}
func TestUpdateImage(t *testing.T) {
someErr := errors.New("error")
testCases := map[string]struct {
updater *stubImageUpdater
newImage string
wantUpdate bool
wantErr bool
}{
"success": {
updater: &stubImageUpdater{
setImage: &unstructured.Unstructured{
Object: map[string]any{
"spec": map[string]any{
"image": "old-image",
},
},
},
},
newImage: "new-image",
wantUpdate: true,
},
"image is the same": {
updater: &stubImageUpdater{
setImage: &unstructured.Unstructured{
Object: map[string]any{
"spec": map[string]any{
"image": "old-image",
},
},
},
},
newImage: "old-image",
},
"getCurrent error": {
updater: &stubImageUpdater{getErr: someErr},
wantErr: true,
},
"update error": {
updater: &stubImageUpdater{
setImage: &unstructured.Unstructured{
Object: map[string]any{
"spec": map[string]any{
"image": "old-image",
},
},
},
updateErr: someErr,
},
newImage: "new-image",
wantErr: true,
},
"no spec": {
updater: &stubImageUpdater{
setImage: &unstructured.Unstructured{
Object: map[string]any{},
},
},
newImage: "new-image",
wantErr: true,
},
"not a map": {
updater: &stubImageUpdater{
setImage: &unstructured.Unstructured{
Object: map[string]any{
"spec": "not a map",
},
},
},
newImage: "new-image",
wantErr: true,
},
"no spec.image": {
updater: &stubImageUpdater{
setImage: &unstructured.Unstructured{
Object: map[string]any{
"spec": map[string]any{},
},
},
},
newImage: "new-image",
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
upgrader := &Upgrader{
dynamicInterface: tc.updater,
outWriter: &bytes.Buffer{},
}
err := upgrader.updateImage(context.Background(), tc.newImage)
if tc.wantErr {
assert.Error(err)
return
}
assert.NoError(err)
if tc.wantUpdate {
assert.Equal(tc.newImage, tc.updater.updatedImage.Object["spec"].(map[string]any)["image"])
} else {
assert.Nil(tc.updater.updatedImage)
}
})
}
}
type stubImageUpdater struct {
setImage *unstructured.Unstructured
updatedImage *unstructured.Unstructured
getErr error
updateErr error
}
func (u *stubImageUpdater) getCurrent(ctx context.Context, name string) (*unstructured.Unstructured, error) {
return u.setImage, u.getErr
}
func (u *stubImageUpdater) update(_ context.Context, updatedImage *unstructured.Unstructured) (*unstructured.Unstructured, error) {
u.updatedImage = updatedImage
return nil, u.updateErr
}