github.com/vmware/govmomi@v0.51.0/simulator/container_virtual_machine_test.go (about) 1 // © Broadcom. All Rights Reserved. 2 // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. 3 // SPDX-License-Identifier: Apache-2.0 4 5 package simulator 6 7 import ( 8 "bytes" 9 "context" 10 "fmt" 11 "log" 12 "os" 13 "os/exec" 14 "path/filepath" 15 "strings" 16 "testing" 17 18 "github.com/stretchr/testify/require" 19 20 "github.com/vmware/govmomi/find" 21 "github.com/vmware/govmomi/object" 22 "github.com/vmware/govmomi/test" 23 "github.com/vmware/govmomi/vim25" 24 "github.com/vmware/govmomi/vim25/types" 25 ) 26 27 // takes a content string to serve from the container and returns ExtraConfig options 28 // to construct container 29 // content - the contents of index.html 30 // port - the port to forward to the container port 80 31 func constructNginxBacking(t *testing.T, content string, port int) []types.BaseOptionValue { 32 dir := t.TempDir() 33 // experience shows that a parent directory created as part of the TempDir call may not have 34 // o+rx, preventing use within a container that doesn't have the same uid 35 for dirpart := dir; dirpart != "/"; dirpart = filepath.Dir(dirpart) { 36 os.Chmod(dirpart, 0755) 37 stat, err := os.Stat(dirpart) 38 require.Nil(t, err, "must be able to check file and directory permissions") 39 require.NotZero(t, stat.Mode()&0005, "does not have o+rx permissions", dirpart) 40 } 41 42 fpath := filepath.Join(dir, "index.html") 43 err := os.WriteFile(fpath, []byte(content), 0644) 44 require.Nil(t, err, "Expected to cleanly write content to file: %s", err) 45 46 // just in case umask gets in the way 47 err = os.Chmod(fpath, 0644) 48 require.Nil(t, err, "Expected to cleanly set file permissions on content: %s", err) 49 50 args := fmt.Sprintf("-v '%s:/usr/share/nginx/html:ro' nginx", dir) 51 52 return []types.BaseOptionValue{ 53 &types.OptionValue{Key: ContainerBackingOptionKey, Value: args}, // run nginx 54 &types.OptionValue{Key: "RUN.port.80", Value: "8888"}, // test port remap 55 } 56 } 57 58 // validates the VM is serving the expected content on the expected ports 59 // pairs with constructNginxBacking 60 func validateNginxContainer(t *testing.T, vm *object.VirtualMachine, expected string, port int) error { 61 ip, _ := vm.WaitForIP(context.Background(), true) // Returns the docker container's IP 62 63 // Count the number of bytes in feature_test.go via nginx going direct to the container 64 cmd := exec.Command("docker", "run", "--rm", "curlimages/curl", "curl", "-f", fmt.Sprintf("http://%s:80", ip)) 65 var buf bytes.Buffer 66 cmd.Stdout = &buf 67 err := cmd.Run() 68 res := buf.String() 69 70 if err != nil || strings.TrimSpace(res) != expected { 71 // we use Fail not Fatal because we want to clean up 72 t.Fail() 73 t.Log(err, buf.String()) 74 fmt.Printf("%d diff", buf.Len()-len(expected)) 75 } 76 77 // Count the number of bytes in feature_test.go via nginx going via port remap on host 78 cmd = exec.Command("curl", "-f", fmt.Sprintf("http://localhost:%d", port)) 79 buf.Reset() 80 cmd.Stdout = &buf 81 err = cmd.Run() 82 res = buf.String() 83 if err != nil || strings.TrimSpace(res) != expected { 84 t.Fail() 85 t.Log(err, buf.String()) 86 fmt.Printf("%d diff", buf.Len()-len(expected)) 87 } 88 89 return nil 90 } 91 92 // 1. Construct ExtraConfig args for container backing 93 // 2. Create VM using that ExtraConfig 94 // 3. Confirm docker container present that matches expectations 95 func TestCreateVMWithContainerBacking(t *testing.T) { 96 Test(func(ctx context.Context, c *vim25.Client) { 97 if !test.HasDocker() { 98 t.Skip("requires docker on linux") 99 return 100 } 101 102 finder := find.NewFinder(c) 103 pool, _ := finder.ResourcePool(ctx, "DC0_H0/Resources") 104 dc, err := finder.Datacenter(ctx, "DC0") 105 if err != nil { 106 log.Fatal(err) 107 } 108 109 content := "foo" 110 port := 8888 111 112 spec := types.VirtualMachineConfigSpec{ 113 Name: "nginx-container-backed-from-creation", 114 Files: &types.VirtualMachineFileInfo{ 115 VmPathName: "[LocalDS_0] nginx", 116 }, 117 ExtraConfig: constructNginxBacking(t, content, port), 118 } 119 120 f, _ := dc.Folders(ctx) 121 // Create a new VM 122 task, err := f.VmFolder.CreateVM(ctx, spec, pool, nil) 123 if err != nil { 124 log.Fatal(err) 125 } 126 127 info, err := task.WaitForResult(ctx, nil) 128 if err != nil { 129 log.Fatal(err) 130 } 131 132 vm := object.NewVirtualMachine(c, info.Result.(types.ManagedObjectReference)) 133 // PowerOn VM starts the nginx container 134 task, _ = vm.PowerOn(ctx) 135 err = task.Wait(ctx) 136 if err != nil { 137 log.Fatal(err) 138 } 139 140 err = validateNginxContainer(t, vm, content, port) 141 if err != nil { 142 log.Fatal(err) 143 } 144 145 spec2 := types.VirtualMachineConfigSpec{ 146 ExtraConfig: []types.BaseOptionValue{ 147 &types.OptionValue{Key: ContainerBackingOptionKey, Value: ""}, 148 }, 149 } 150 151 task, err = vm.Reconfigure(ctx, spec2) 152 if err != nil { 153 log.Fatal(err) 154 } 155 156 info, err = task.WaitForResult(ctx, nil) 157 if err != nil { 158 log.Fatal(info, err) 159 } 160 161 // PowerOff stops the container 162 task, _ = vm.PowerOff(ctx) 163 _ = task.Wait(ctx) 164 // Destroy deletes the container 165 task, _ = vm.Destroy(ctx) 166 _ = task.Wait(ctx) 167 }) 168 } 169 170 // 1. Create VM without ExtraConfig args for container backing 171 // 2. Construct ExtraConfig args for container backing 172 // 3. Update VM with ExtraConfig 173 // 4. Confirm docker container present that matches expectations 174 func TestUpdateVMAddContainerBacking(t *testing.T) { 175 Test(func(ctx context.Context, c *vim25.Client) { 176 if !test.HasDocker() { 177 t.Skip("requires docker on linux") 178 return 179 } 180 181 finder := find.NewFinder(c) 182 pool, _ := finder.ResourcePool(ctx, "DC0_H0/Resources") 183 dc, err := finder.Datacenter(ctx, "DC0") 184 if err != nil { 185 log.Fatal(err) 186 } 187 188 content := "foo" 189 port := 8888 190 191 spec := types.VirtualMachineConfigSpec{ 192 Name: "nginx-container-after-reconfig", 193 Files: &types.VirtualMachineFileInfo{ 194 VmPathName: "[LocalDS_0] nginx", 195 }, 196 } 197 198 f, _ := dc.Folders(ctx) 199 // Create a new VM 200 task, err := f.VmFolder.CreateVM(ctx, spec, pool, nil) 201 if err != nil { 202 log.Fatal(err) 203 } 204 205 info, err := task.WaitForResult(ctx, nil) 206 if err != nil { 207 log.Fatal(err) 208 } 209 210 vm := object.NewVirtualMachine(c, info.Result.(types.ManagedObjectReference)) 211 // PowerOn VM starts the nginx container 212 task, _ = vm.PowerOn(ctx) 213 err = task.Wait(ctx) 214 if err != nil { 215 log.Fatal(err) 216 } 217 218 spec2 := types.VirtualMachineConfigSpec{ 219 ExtraConfig: constructNginxBacking(t, content, port), 220 } 221 222 task, err = vm.Reconfigure(ctx, spec2) 223 if err != nil { 224 log.Fatal(err) 225 } 226 227 info, err = task.WaitForResult(ctx, nil) 228 if err != nil { 229 log.Fatal(info, err) 230 } 231 232 err = validateNginxContainer(t, vm, content, port) 233 if err != nil { 234 log.Fatal(err) 235 } 236 237 // PowerOff stops the container 238 task, _ = vm.PowerOff(ctx) 239 _ = task.Wait(ctx) 240 // Destroy deletes the container 241 task, _ = vm.Destroy(ctx) 242 _ = task.Wait(ctx) 243 }) 244 }