gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/runsc/cmd/install_test.go (about)

     1  // Copyright 2022 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package cmd
    16  
    17  import (
    18  	"encoding/json"
    19  	"fmt"
    20  	"testing"
    21  
    22  	"github.com/google/go-cmp/cmp"
    23  )
    24  
    25  type runtimeDef struct {
    26  	path        string
    27  	runtimeArgs []string
    28  }
    29  
    30  func (r *runtimeDef) MarshalJSON() ([]byte, error) {
    31  	args, err := json.Marshal(r.runtimeArgs)
    32  	if err != nil {
    33  		return nil, err
    34  	}
    35  	str := fmt.Sprintf(`{"path": "%s", "runtimeArgs":%s}`, r.path, args)
    36  	return []byte(str), nil
    37  }
    38  
    39  func (r *runtimeDef) UnmarshalJSON(data []byte) error {
    40  	var dat map[string]any
    41  	if err := json.Unmarshal(data, &dat); err != nil {
    42  		return err
    43  	}
    44  	if p, ok := dat["path"]; ok {
    45  		r.path = p.(string)
    46  	}
    47  	if p, ok := dat["runtimeArgs"]; ok {
    48  		r.runtimeArgs = p.([]string)
    49  	}
    50  	return nil
    51  }
    52  
    53  var defaultInput = map[string]any{
    54  	"runtimes": map[string]*runtimeDef{
    55  		"runtime1": &runtimeDef{
    56  			path:        "runtime1_path",
    57  			runtimeArgs: []string{"some", "args"},
    58  		},
    59  		"other runtime": &runtimeDef{
    60  			path:        "other_runtime_path",
    61  			runtimeArgs: []string{"some", "other", "args"},
    62  		},
    63  		"myRuntime": &runtimeDef{
    64  			path:        "myRuntimePath",
    65  			runtimeArgs: []string{"super", "cool", "args"},
    66  		},
    67  	},
    68  	"exec-opts": []string{"some-cgroup-driver=something", "native.cgroupdriver=init_driver"},
    69  }
    70  
    71  func TestInstall(t *testing.T) {
    72  
    73  	for _, tc := range []struct {
    74  		name   string
    75  		i      *Install
    76  		input  map[string]any
    77  		output map[string]any
    78  	}{
    79  		{
    80  			name: "clobber",
    81  			i: &Install{
    82  				Runtime:        "myRuntime",
    83  				Experimental:   true,
    84  				Clobber:        true,
    85  				CgroupDriver:   "my_driver",
    86  				executablePath: "some_runsc_path",
    87  				runtimeArgs:    []string{"new", "cool", "args"},
    88  			},
    89  			input: defaultInput,
    90  			output: map[string]any{
    91  				"runtimes": map[string]*runtimeDef{
    92  					"runtime1": &runtimeDef{
    93  						path:        "runtime1_path",
    94  						runtimeArgs: []string{"some", "args"},
    95  					},
    96  					"other runtime": &runtimeDef{
    97  						path:        "other_runtime_path",
    98  						runtimeArgs: []string{"some", "other", "args"},
    99  					},
   100  					"myRuntime": &runtimeDef{
   101  						path:        "some_runsc_path",
   102  						runtimeArgs: []string{"new", "cool", "args"},
   103  					},
   104  				},
   105  				"exec-opts":    []string{"some-cgroup-driver=something", "native.cgroupdriver=my_driver"},
   106  				"experimental": true,
   107  			},
   108  		},
   109  		{
   110  			name: "no clobber",
   111  			i: &Install{
   112  				Runtime:        "myRuntime",
   113  				Experimental:   true,
   114  				Clobber:        false,
   115  				CgroupDriver:   "my_driver",
   116  				executablePath: "some_runsc_path",
   117  				runtimeArgs:    []string{"new", "cool", "args"},
   118  			},
   119  			input: defaultInput,
   120  			output: map[string]any{
   121  				"runtimes": map[string]*runtimeDef{
   122  					"runtime1": &runtimeDef{
   123  						path:        "runtime1_path",
   124  						runtimeArgs: []string{"some", "args"},
   125  					},
   126  					"other runtime": &runtimeDef{
   127  						path:        "other_runtime_path",
   128  						runtimeArgs: []string{"some", "other", "args"},
   129  					},
   130  					"myRuntime": &runtimeDef{
   131  						path:        "myRuntimePath",
   132  						runtimeArgs: []string{"super", "cool", "args"},
   133  					},
   134  				},
   135  				"exec-opts":    []string{"some-cgroup-driver=something", "native.cgroupdriver=init_driver", "native.cgroupdriver=my_driver"},
   136  				"experimental": true,
   137  			},
   138  		},
   139  		{
   140  			name: "new runtime",
   141  			i: &Install{
   142  				Runtime:        "newRuntime",
   143  				Experimental:   true,
   144  				executablePath: "newPath",
   145  				runtimeArgs:    []string{"new", "cool", "args"},
   146  			},
   147  			input: defaultInput,
   148  			output: map[string]any{
   149  				"runtimes": map[string]*runtimeDef{
   150  					"runtime1": &runtimeDef{
   151  						path:        "runtime1_path",
   152  						runtimeArgs: []string{"some", "args"},
   153  					},
   154  					"newRuntime": &runtimeDef{
   155  						path:        "newPath",
   156  						runtimeArgs: []string{"new", "cool", "args"},
   157  					},
   158  					"other runtime": &runtimeDef{
   159  						path:        "other_runtime_path",
   160  						runtimeArgs: []string{"some", "other", "args"},
   161  					},
   162  					"myRuntime": &runtimeDef{
   163  						path:        "myRuntimePath",
   164  						runtimeArgs: []string{"super", "cool", "args"},
   165  					},
   166  				},
   167  				"exec-opts":    []string{"some-cgroup-driver=something", "native.cgroupdriver=init_driver"},
   168  				"experimental": true,
   169  			},
   170  		},
   171  	} {
   172  		t.Run(tc.name, func(t *testing.T) {
   173  
   174  			mockRead := func(_ string) ([]byte, error) {
   175  				return json.MarshalIndent(tc.input, "", "  ")
   176  			}
   177  
   178  			got := []byte{}
   179  			mockWrite := func(c map[string]any, _ string) error {
   180  				res, err := json.MarshalIndent(c, "", "  ")
   181  				if err != nil {
   182  					return err
   183  				}
   184  				got = res
   185  				return nil
   186  			}
   187  
   188  			rw := configReaderWriter{
   189  				read:  mockRead,
   190  				write: mockWrite,
   191  			}
   192  
   193  			if err := doInstallConfig(tc.i, rw); err != nil {
   194  				t.Fatalf("Error updating config: %v", err)
   195  			}
   196  
   197  			want, err := json.MarshalIndent(tc.output, "", "  ")
   198  			if err != nil {
   199  				t.Fatalf("Failed to marshal output: %v", err)
   200  			}
   201  
   202  			if res := cmp.Diff(string(want), string(got)); res != "" {
   203  				t.Fatalf("Mismatch output (-want +got): %s", res)
   204  			}
   205  		})
   206  	}
   207  }
   208  
   209  func TestUninstall(t *testing.T) {
   210  	for _, tc := range []struct {
   211  		name    string
   212  		u       *Uninstall
   213  		input   map[string]any
   214  		output  map[string]any
   215  		wantErr bool
   216  	}{
   217  		{
   218  			name: "runtime found",
   219  			u: &Uninstall{
   220  				Runtime: "other runtime",
   221  			},
   222  			input: defaultInput,
   223  			output: map[string]any{
   224  				"runtimes": map[string]*runtimeDef{
   225  					"runtime1": &runtimeDef{
   226  						path:        "runtime1_path",
   227  						runtimeArgs: []string{"some", "args"},
   228  					},
   229  					"myRuntime": &runtimeDef{
   230  						path:        "myRuntimePath",
   231  						runtimeArgs: []string{"super", "cool", "args"},
   232  					},
   233  				},
   234  				"exec-opts": []string{"some-cgroup-driver=something", "native.cgroupdriver=init_driver"},
   235  			},
   236  		},
   237  		{
   238  			name: "runtime not found",
   239  			u: &Uninstall{
   240  				Runtime: "not found runtime",
   241  			},
   242  			input:   defaultInput,
   243  			wantErr: true,
   244  		},
   245  	} {
   246  		t.Run(tc.name, func(t *testing.T) {
   247  			mockRead := func(_ string) ([]byte, error) {
   248  				return json.MarshalIndent(tc.input, "", "  ")
   249  			}
   250  
   251  			got := []byte{}
   252  			mockWrite := func(c map[string]any, _ string) error {
   253  				res, err := json.MarshalIndent(c, "", "  ")
   254  				if err != nil {
   255  					return err
   256  				}
   257  				got = res
   258  				return nil
   259  			}
   260  
   261  			rw := configReaderWriter{
   262  				read:  mockRead,
   263  				write: mockWrite,
   264  			}
   265  
   266  			err := doUninstallConfig(tc.u, rw)
   267  			if tc.wantErr {
   268  				if err == nil {
   269  					t.Fatalf("Did not get an error when expected.")
   270  				}
   271  				return
   272  			}
   273  			if err != nil {
   274  				t.Fatalf("Error updating config: %v", err)
   275  			}
   276  
   277  			want, err := json.MarshalIndent(tc.output, "", "  ")
   278  			if err != nil {
   279  				t.Fatalf("Failed to marshal output: %v", err)
   280  			}
   281  			if res := cmp.Diff(string(want), string(got)); res != "" {
   282  				t.Fatalf("Mismatch output (-want +got-): %s", res)
   283  			}
   284  		})
   285  	}
   286  }