...

Source file src/edge-infra.dev/pkg/sds/lib/k8s/manifest/manifest_test.go

Documentation: edge-infra.dev/pkg/sds/lib/k8s/manifest

     1  package manifest
     2  
     3  import (
     4  	"bytes"
     5  	_ "embed"
     6  	"errors"
     7  	"io/fs"
     8  	"os"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/spf13/afero"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  	corev1 "k8s.io/api/core/v1"
    16  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    17  	"k8s.io/apimachinery/pkg/runtime"
    18  )
    19  
    20  var (
    21  	//go:embed testdata/test-pod.yaml
    22  	podBytes []byte
    23  
    24  	//go:embed testdata/embed/updated-test-pod.yaml
    25  	updatedPodBytes []byte
    26  )
    27  
    28  var podObject = &corev1.Pod{
    29  	TypeMeta: metav1.TypeMeta{
    30  		APIVersion: "v1",
    31  		Kind:       "Pod",
    32  	},
    33  	ObjectMeta: metav1.ObjectMeta{
    34  		Name: "test-pod",
    35  	},
    36  	Spec: corev1.PodSpec{
    37  		Containers: []corev1.Container{
    38  			{
    39  				Name:  "test-container",
    40  				Image: "test-image:1.14.2",
    41  				Ports: []corev1.ContainerPort{
    42  					{
    43  						ContainerPort: 80,
    44  					},
    45  				},
    46  			},
    47  		},
    48  	},
    49  }
    50  
    51  func updatedPodObject() *corev1.Pod {
    52  	pod := podObject.DeepCopy()
    53  	pod.Name = "updated-test-pod"
    54  	return pod
    55  }
    56  
    57  var (
    58  	testPodReadpath    = "/test-pod.yaml"
    59  	testPodWritepath   = "/test-pod-write.yaml"
    60  	invalidPodFilepath = "/invalid.yaml"
    61  )
    62  
    63  func TestMain(m *testing.M) {
    64  	os.Exit(m.Run())
    65  }
    66  
    67  func TestRead(t *testing.T) {
    68  	memFs, err := createMemFs(afero.NewOsFs())
    69  	require.NoError(t, err)
    70  
    71  	testCases := map[string]struct {
    72  		manifest      Manifest
    73  		syncMode      bool
    74  		expectedBytes []byte
    75  		expectedMode  fs.FileMode
    76  		expectError   bool
    77  	}{
    78  		"WithModeSync_Success": {
    79  			manifest:      New(memFs, testPodReadpath, &corev1.Pod{}, 0666),
    80  			syncMode:      true,
    81  			expectedBytes: podBytes,
    82  			expectedMode:  fs.FileMode(0660),
    83  			expectError:   false,
    84  		},
    85  		"WithoutModeSync_Success": {
    86  			manifest:      New(memFs, testPodReadpath, &corev1.Pod{}, 0666),
    87  			syncMode:      false,
    88  			expectedBytes: podBytes,
    89  			expectedMode:  fs.FileMode(0666),
    90  			expectError:   false,
    91  		},
    92  		"FileNotFound_Failure": {
    93  			manifest:    New(memFs, invalidPodFilepath, &corev1.Pod{}, 0666),
    94  			expectError: true,
    95  		},
    96  	}
    97  
    98  	for name, tc := range testCases {
    99  		t.Run(name, func(t *testing.T) {
   100  			err := tc.manifest.Read(tc.syncMode)
   101  			if tc.expectError {
   102  				require.Error(t, err)
   103  				return
   104  			}
   105  			require.NoError(t, err)
   106  
   107  			gotBytes, err := tc.manifest.Bytes()
   108  			require.NoError(t, err)
   109  
   110  			assert.Equal(t, tc.expectedBytes, gotBytes)
   111  			assert.Equal(t, tc.expectedMode, tc.manifest.mode)
   112  		})
   113  	}
   114  }
   115  
   116  func TestLoad(t *testing.T) {
   117  	testCases := map[string]struct {
   118  		manifest    Manifest
   119  		inputBytes  []byte
   120  		expectedObj *corev1.Pod
   121  		expectError bool
   122  	}{
   123  		"Success": {
   124  			manifest:    New(nil, "", &corev1.Pod{}, 0),
   125  			inputBytes:  podBytes,
   126  			expectedObj: podObject,
   127  			expectError: false,
   128  		},
   129  		"InvalidYAML_Failure": {
   130  			manifest:    New(nil, "", &corev1.Pod{}, 0),
   131  			inputBytes:  []byte("hello"),
   132  			expectError: true,
   133  		},
   134  		"NoManifests_Failure": {
   135  			manifest:    New(nil, "", &corev1.Pod{}, 0),
   136  			inputBytes:  []byte{},
   137  			expectError: true,
   138  		},
   139  	}
   140  
   141  	for name, tc := range testCases {
   142  		t.Run(name, func(t *testing.T) {
   143  			err := tc.manifest.Load(tc.inputBytes)
   144  			if tc.expectError {
   145  				require.Error(t, err)
   146  				return
   147  			}
   148  			require.NoError(t, err)
   149  
   150  			gotObj := tc.manifest.Content().(*corev1.Pod)
   151  			assert.Equal(t, tc.expectedObj, gotObj)
   152  		})
   153  	}
   154  }
   155  
   156  func TestWrite(t *testing.T) {
   157  	memFs, err := createMemFs(afero.NewOsFs())
   158  	require.NoError(t, err)
   159  
   160  	testCases := map[string]struct {
   161  		manifest      Manifest
   162  		inputBytes    []byte
   163  		expectedBytes []byte
   164  		expectedMode  fs.FileMode
   165  		expectError   bool
   166  	}{
   167  		"Success": {
   168  			manifest:      New(memFs, testPodWritepath, &corev1.Pod{}, 0666),
   169  			inputBytes:    podBytes,
   170  			expectedBytes: podBytes,
   171  			expectedMode:  fs.FileMode(0666),
   172  			expectError:   false,
   173  		},
   174  		"NoBaseObject_Failure": {
   175  			manifest:    New(memFs, testPodWritepath, nil, 0666),
   176  			inputBytes:  []byte{},
   177  			expectError: true,
   178  		},
   179  	}
   180  
   181  	for name, tc := range testCases {
   182  		t.Run(name, func(t *testing.T) {
   183  			if !bytes.Equal(tc.inputBytes, []byte{}) {
   184  				err := tc.manifest.Load(tc.inputBytes)
   185  				require.NoError(t, err)
   186  			}
   187  
   188  			err := tc.manifest.Write()
   189  			if tc.expectError {
   190  				require.Error(t, err)
   191  				return
   192  			}
   193  			require.NoError(t, err)
   194  
   195  			gotBytes, err := afero.ReadFile(memFs, testPodWritepath)
   196  			require.NoError(t, err)
   197  
   198  			assert.Equal(t, tc.expectedBytes, gotBytes)
   199  			assert.Equal(t, tc.expectedMode, tc.manifest.mode)
   200  
   201  			require.NoError(t, memFs.RemoveAll(testPodWritepath))
   202  		})
   203  	}
   204  }
   205  
   206  func TestWithCreate(t *testing.T) {
   207  	memFs, err := createMemFs(afero.NewOsFs())
   208  	require.NoError(t, err)
   209  
   210  	testCases := map[string]struct {
   211  		manifest      Manifest
   212  		fn            func(runtime.Object) error
   213  		expectedBytes []byte
   214  		expectedMode  fs.FileMode
   215  		expectError   bool
   216  	}{
   217  		"DefaultMode_Success": {
   218  			manifest: New(memFs, testPodWritepath, &corev1.Pod{}, 0),
   219  			fn: func(obj runtime.Object) error {
   220  				podObject.DeepCopyInto(obj.(*corev1.Pod))
   221  				return nil
   222  			},
   223  			expectedBytes: podBytes,
   224  			expectedMode:  fs.FileMode(0600),
   225  			expectError:   false,
   226  		},
   227  		"OverwriteMode_Success": {
   228  			manifest: New(memFs, testPodWritepath, &corev1.Pod{}, 0666),
   229  			fn: func(obj runtime.Object) error {
   230  				podObject.DeepCopyInto(obj.(*corev1.Pod))
   231  				return nil
   232  			},
   233  			expectedBytes: podBytes,
   234  			expectedMode:  fs.FileMode(0666),
   235  			expectError:   false,
   236  		},
   237  		"Failure": {
   238  			manifest: New(memFs, testPodWritepath, nil, 0666),
   239  			fn: func(_ runtime.Object) error {
   240  				return errors.New("force error")
   241  			},
   242  			expectError: true,
   243  		},
   244  	}
   245  
   246  	for name, tc := range testCases {
   247  		t.Run(name, func(t *testing.T) {
   248  			err := tc.manifest.WithCreate(tc.fn)
   249  			switch tc.expectError {
   250  			case true:
   251  				require.Error(t, err)
   252  			case false:
   253  				require.NoError(t, err)
   254  			}
   255  
   256  			gotBytes, err := afero.ReadFile(memFs, testPodWritepath)
   257  			switch tc.expectError {
   258  			case true:
   259  				require.Error(t, err)
   260  				return
   261  			case false:
   262  				require.NoError(t, err)
   263  			}
   264  
   265  			assert.Equal(t, tc.expectedBytes, gotBytes)
   266  			assert.Equal(t, tc.expectedMode, tc.manifest.mode)
   267  
   268  			require.NoError(t, memFs.RemoveAll(testPodWritepath))
   269  		})
   270  	}
   271  }
   272  
   273  func TestWithUpdate(t *testing.T) {
   274  	memFs, err := createMemFs(afero.NewOsFs())
   275  	require.NoError(t, err)
   276  
   277  	testCases := map[string]struct {
   278  		manifest      Manifest
   279  		fn            func(runtime.Object) error
   280  		expectedBytes []byte
   281  		expectedMode  fs.FileMode
   282  		expectError   bool
   283  	}{
   284  		"PersistMode_Success": {
   285  			manifest: New(memFs, testPodWritepath, &corev1.Pod{}, 0),
   286  			fn: func(obj runtime.Object) error {
   287  				updatedPodObject().DeepCopyInto(obj.(*corev1.Pod))
   288  				return nil
   289  			},
   290  			expectedBytes: updatedPodBytes,
   291  			expectedMode:  fs.FileMode(0660),
   292  			expectError:   false,
   293  		},
   294  		"OverwriteMode_Success": {
   295  			manifest: New(memFs, testPodWritepath, &corev1.Pod{}, 0666),
   296  			fn: func(obj runtime.Object) error {
   297  				updatedPodObject().DeepCopyInto(obj.(*corev1.Pod))
   298  				return nil
   299  			},
   300  			expectedBytes: updatedPodBytes,
   301  			expectedMode:  fs.FileMode(0666),
   302  			expectError:   false,
   303  		},
   304  		"Failure": {
   305  			manifest: New(memFs, testPodWritepath, &corev1.Pod{}, 0666),
   306  			fn: func(_ runtime.Object) error {
   307  				return errors.New("force error")
   308  			},
   309  			expectedBytes: podBytes,
   310  			expectError:   true,
   311  		},
   312  	}
   313  
   314  	for name, tc := range testCases {
   315  		t.Run(name, func(t *testing.T) {
   316  			require.NoError(t, afero.WriteFile(memFs, testPodWritepath, podBytes, 0660))
   317  
   318  			err := tc.manifest.WithUpdate(tc.fn)
   319  			switch tc.expectError {
   320  			case true:
   321  				require.Error(t, err)
   322  			case false:
   323  				require.NoError(t, err)
   324  			}
   325  
   326  			gotBytes, err := afero.ReadFile(memFs, testPodWritepath)
   327  			switch tc.expectError {
   328  			case true:
   329  				assert.Equal(t, tc.expectedBytes, gotBytes)
   330  				return
   331  			case false:
   332  				require.NoError(t, err)
   333  			}
   334  
   335  			assert.Equal(t, tc.expectedBytes, gotBytes)
   336  			assert.Equal(t, tc.expectedMode, tc.manifest.mode)
   337  
   338  			require.NoError(t, memFs.RemoveAll(testPodWritepath))
   339  		})
   340  	}
   341  }
   342  
   343  func createMemFs(osFs afero.Fs) (afero.Fs, error) {
   344  	memFs := afero.NewMemMapFs()
   345  	return memFs, afero.Walk(osFs, "testdata", func(path string, info fs.FileInfo, err error) error {
   346  		if strings.Contains(path, "/embed/") {
   347  			return nil
   348  		}
   349  		if err != nil {
   350  			return err
   351  		}
   352  		fsPath := strings.ReplaceAll(path, "testdata", "")
   353  		if info.IsDir() {
   354  			return memFs.MkdirAll(fsPath, info.Mode())
   355  		}
   356  		content, err := afero.ReadFile(osFs, path)
   357  		if err != nil {
   358  			return err
   359  		}
   360  		return afero.WriteFile(memFs, fsPath, content, 0660)
   361  	})
   362  }
   363  

View as plain text