github.com/cloudfoundry-attic/garden-linux@v0.333.2-candidate/integration/lifecycle/rootfs_test.go (about)

     1  package lifecycle_test
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"net/http/httputil"
     9  	"net/url"
    10  	"os"
    11  	"os/exec"
    12  	"path/filepath"
    13  
    14  	"github.com/cloudfoundry-incubator/garden"
    15  
    16  	. "github.com/onsi/ginkgo"
    17  	. "github.com/onsi/gomega"
    18  	"github.com/onsi/gomega/types"
    19  )
    20  
    21  var (
    22  	dockerRegistryRootFSPath   = os.Getenv("GARDEN_DOCKER_REGISTRY_TEST_ROOTFS")
    23  	dockerRegistryV2RootFSPath = os.Getenv("GARDEN_DOCKER_REGISTRY_V2_TEST_ROOTFS")
    24  )
    25  
    26  var _ = Describe("Rootfs container create parameter", func() {
    27  	var container garden.Container
    28  	var args []string
    29  
    30  	BeforeEach(func() {
    31  		container = nil
    32  		args = []string{}
    33  	})
    34  
    35  	JustBeforeEach(func() {
    36  		client = startGarden(args...)
    37  	})
    38  
    39  	AfterEach(func() {
    40  		if container != nil {
    41  			Expect(client.Destroy(container.Handle())).To(Succeed())
    42  		}
    43  	})
    44  
    45  	Context("without a default rootfs", func() {
    46  		BeforeEach(func() {
    47  			args = []string{"--rootfs", ""}
    48  		})
    49  
    50  		It("without a rootfs in container spec, the container creation fails", func() {
    51  			var err error
    52  
    53  			container, err = client.Create(garden.ContainerSpec{RootFSPath: ""})
    54  			Ω(err).Should(HaveOccurred())
    55  			Ω(err).Should(MatchError(ContainSubstring(
    56  				"RootFSPath: is a required parameter, since no default rootfs was provided to the server. To provide a default rootfs, use the --rootfs flag on startup.",
    57  			)))
    58  		})
    59  
    60  		It("with a rootfs in container spec, the container is created successfully", func() {
    61  			var err error
    62  
    63  			container, err = client.Create(garden.ContainerSpec{RootFSPath: os.Getenv("GARDEN_TEST_ROOTFS")})
    64  			Ω(err).ShouldNot(HaveOccurred())
    65  		})
    66  	})
    67  
    68  	Context("with a default rootfs", func() {
    69  		It("the container is created successfully", func() {
    70  			var err error
    71  
    72  			container, err = client.Create(garden.ContainerSpec{RootFSPath: ""})
    73  			Expect(err).ToNot(HaveOccurred())
    74  		})
    75  	})
    76  
    77  	Context("with a docker rootfs URI", func() {
    78  		Context("not containing a host", func() {
    79  			It("succesfully creates the container", func() {
    80  				var err error
    81  
    82  				container, err = client.Create(garden.ContainerSpec{RootFSPath: "docker:///busybox"})
    83  				Expect(err).ToNot(HaveOccurred())
    84  			})
    85  
    86  			Context("when image does not exist", func() {
    87  				It("should not leak the depot directory", func() {
    88  					_, err := client.Create(
    89  						garden.ContainerSpec{
    90  							RootFSPath: "docker:///cfgarden/doesnotexist",
    91  						},
    92  					)
    93  					Expect(err).To(HaveOccurred())
    94  
    95  					entries, err := ioutil.ReadDir(client.DepotPath)
    96  					Expect(err).ToNot(HaveOccurred())
    97  					Expect(entries).To(HaveLen(0))
    98  				})
    99  			})
   100  
   101  			Context("when the -registry flag targets a non-existing registry", func() {
   102  				BeforeEach(func() {
   103  					args = []string{"--registry", "registry-12.banana-docker.io"}
   104  				})
   105  
   106  				It("should fail to create a container", func() {
   107  					var err error
   108  
   109  					container, err = client.Create(garden.ContainerSpec{RootFSPath: "docker:///busybox"})
   110  					Expect(err).To(HaveOccurred())
   111  				})
   112  			})
   113  		})
   114  
   115  		Context("containing a host", func() {
   116  			Context("which is valid", func() {
   117  				It("creates the container successfully", func() {
   118  					var err error
   119  
   120  					container, err = client.Create(garden.ContainerSpec{RootFSPath: "docker://registry-1.docker.io/busybox"})
   121  					Expect(err).ToNot(HaveOccurred())
   122  				})
   123  			})
   124  
   125  			Context("which is invalid", func() {
   126  				It("the container is not created successfully", func() {
   127  					var err error
   128  					container, err = client.Create(garden.ContainerSpec{RootFSPath: "docker://xindex.docker.io/busybox"})
   129  					Expect(err).To(HaveOccurred())
   130  				})
   131  			})
   132  
   133  			Context("which is insecure", func() {
   134  				var (
   135  					dockerRegistry     garden.Container
   136  					dockerRegistryIP   string
   137  					dockerRegistryPort string
   138  				)
   139  
   140  				BeforeEach(func() {
   141  					dockerRegistryIP = "10.0.0.2"
   142  					dockerRegistryPort = "5000"
   143  				})
   144  
   145  				JustBeforeEach(func() {
   146  					if dockerRegistryV2RootFSPath == "" {
   147  						Skip("GARDEN_DOCKER_REGISTRY_V2_TEST_ROOTFS undefined")
   148  					}
   149  
   150  					dockerRegistry = startV2DockerRegistry(dockerRegistryIP, dockerRegistryPort)
   151  				})
   152  
   153  				AfterEach(func() {
   154  					if dockerRegistry != nil {
   155  						Expect(client.Destroy(dockerRegistry.Handle())).To(Succeed())
   156  					}
   157  				})
   158  
   159  				Context("when the host is listed in -insecureDockerRegistry", func() {
   160  					BeforeEach(func() {
   161  						args = []string{
   162  							"-allowHostAccess=true",
   163  						}
   164  					})
   165  
   166  					Context("when the registry is NOT using TLS", func() {
   167  						BeforeEach(func() {
   168  							args = append(
   169  								args,
   170  								"-insecureDockerRegistry",
   171  								fmt.Sprintf("%s:%s", dockerRegistryIP, dockerRegistryPort),
   172  							)
   173  						})
   174  
   175  						It("creates the container successfully ", func() {
   176  							_, err := client.Create(garden.ContainerSpec{
   177  								RootFSPath: fmt.Sprintf("docker://%s:%s/busybox", dockerRegistryIP,
   178  									dockerRegistryPort),
   179  							})
   180  							Expect(err).ToNot(HaveOccurred())
   181  						})
   182  					})
   183  
   184  					Context("when the registry is in a CIDR", func() {
   185  						BeforeEach(func() {
   186  							args = append(
   187  								args,
   188  								"-insecureDockerRegistry",
   189  								fmt.Sprintf("%s/24", dockerRegistryIP),
   190  							)
   191  						})
   192  
   193  						It("creates the container successfully ", func() {
   194  							_, err := client.Create(garden.ContainerSpec{
   195  								RootFSPath: fmt.Sprintf("docker://%s:%s/busybox", dockerRegistryIP, dockerRegistryPort),
   196  							})
   197  							Expect(err).ToNot(HaveOccurred())
   198  						})
   199  					})
   200  
   201  					Context("when the registry is using TLS", func() {
   202  						var server *httptest.Server
   203  						var serverURL *url.URL
   204  
   205  						BeforeEach(func() {
   206  							proxyTo, err := url.Parse(fmt.Sprintf("http://%s:%s", dockerRegistryIP,
   207  								dockerRegistryPort))
   208  							Expect(err).NotTo(HaveOccurred())
   209  
   210  							server = httptest.NewTLSServer(httputil.NewSingleHostReverseProxy(proxyTo))
   211  							serverURL, err = url.Parse(server.URL)
   212  							Expect(err).NotTo(HaveOccurred())
   213  
   214  							args = append(
   215  								args,
   216  								"-insecureDockerRegistry",
   217  								serverURL.Host,
   218  							)
   219  						})
   220  
   221  						AfterEach(func() {
   222  							server.Close()
   223  						})
   224  
   225  						It("creates the container successfully", func() {
   226  							_, err := client.Create(garden.ContainerSpec{
   227  								RootFSPath: fmt.Sprintf("docker://%s/busybox", serverURL.Host),
   228  							})
   229  							Expect(err).ToNot(HaveOccurred())
   230  						})
   231  
   232  						Context("and its specified as --registry", func() {
   233  							BeforeEach(func() {
   234  								args = append(args, "--registry", serverURL.Host)
   235  							})
   236  
   237  							It("still works when the host is specified", func() {
   238  								_, err := client.Create(garden.ContainerSpec{
   239  									RootFSPath: fmt.Sprintf("docker://%s/busybox", serverURL.Host),
   240  								})
   241  								Expect(err).ToNot(HaveOccurred())
   242  							})
   243  
   244  							It("still works using the default host", func() {
   245  								_, err := client.Create(garden.ContainerSpec{
   246  									RootFSPath: fmt.Sprintf("docker:///busybox"),
   247  								})
   248  								Expect(err).ToNot(HaveOccurred())
   249  							})
   250  						})
   251  					})
   252  				})
   253  
   254  				Context("when the host is NOT listed in -insecureDockerRegistry", func() {
   255  					It("fails", func() {
   256  						_, err := client.Create(garden.ContainerSpec{
   257  							RootFSPath: fmt.Sprintf("docker://%s:%s/busybox", dockerRegistryIP,
   258  								dockerRegistryPort),
   259  						})
   260  
   261  						Expect(err).To(HaveOccurred())
   262  					})
   263  				})
   264  			})
   265  		})
   266  	})
   267  
   268  	Context("when the modified timestamp of the rootfs top-level directory changes", func() {
   269  		var (
   270  			rootfspath          string
   271  			privilegedContainer bool
   272  			container2          garden.Container
   273  		)
   274  
   275  		JustBeforeEach(func() {
   276  			var err error
   277  			rootfspath = createSmallRootfs()
   278  
   279  			container, err = client.Create(garden.ContainerSpec{
   280  				RootFSPath: rootfspath,
   281  				Privileged: privilegedContainer,
   282  			})
   283  			Expect(err).NotTo(HaveOccurred())
   284  
   285  			// ls is convenient, but any file modification is sufficient
   286  			ls := filepath.Join(rootfspath, "bin", "ls")
   287  			Expect(exec.Command("cp", ls, rootfspath).Run()).To(Succeed())
   288  
   289  			container2, err = client.Create(garden.ContainerSpec{
   290  				RootFSPath: rootfspath,
   291  				Privileged: privilegedContainer,
   292  			})
   293  			Expect(err).NotTo(HaveOccurred())
   294  		})
   295  
   296  		AfterEach(func() {
   297  			if container2 != nil {
   298  				Expect(client.Destroy(container2.Handle())).To(Succeed())
   299  			}
   300  		})
   301  
   302  		Context("with a non-privileged container", func() {
   303  			BeforeEach(func() {
   304  				privilegedContainer = false
   305  			})
   306  
   307  			It("should use the updated rootfs when creating a new container", func() {
   308  				process, err := container2.Run(garden.ProcessSpec{
   309  					Path: "/ls",
   310  					User: "root",
   311  				}, garden.ProcessIO{Stdout: GinkgoWriter, Stderr: GinkgoWriter})
   312  				Expect(err).NotTo(HaveOccurred())
   313  
   314  				exitStatus, err := process.Wait()
   315  				Expect(err).NotTo(HaveOccurred())
   316  				Expect(exitStatus).To(Equal(0))
   317  			})
   318  		})
   319  	})
   320  
   321  })
   322  
   323  func startV2DockerRegistry(dockerRegistryIP string, dockerRegistryPort string) garden.Container {
   324  	dockerRegistry, err := client.Create(
   325  		garden.ContainerSpec{
   326  			RootFSPath: dockerRegistryV2RootFSPath,
   327  			Network:    dockerRegistryIP,
   328  		},
   329  	)
   330  	Expect(err).ToNot(HaveOccurred())
   331  
   332  	_, err = dockerRegistry.Run(garden.ProcessSpec{
   333  		User: "root",
   334  		Env: []string{
   335  			"REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY=/opt/docker-registry",
   336  		},
   337  		Path: "/go/bin/registry",
   338  		Args: []string{"/go/src/github.com/docker/distribution/cmd/registry/config.yml"},
   339  	}, garden.ProcessIO{Stdout: GinkgoWriter, Stderr: GinkgoWriter})
   340  	Expect(err).ToNot(HaveOccurred())
   341  
   342  	Eventually(
   343  		fmt.Sprintf("http://%s:%s/v2/", dockerRegistryIP, dockerRegistryPort),
   344  		"60s",
   345  	).Should(RespondToGETWith(200))
   346  
   347  	return dockerRegistry
   348  }
   349  
   350  type statusMatcher struct {
   351  	expectedStatus int
   352  
   353  	httpError    error
   354  	actualStatus int
   355  }
   356  
   357  func RespondToGETWith(expected int) types.GomegaMatcher {
   358  	return &statusMatcher{expected, nil, 200}
   359  }
   360  
   361  func (m *statusMatcher) Match(actual interface{}) (success bool, err error) {
   362  	response, err := http.Get(fmt.Sprintf("%s", actual))
   363  	if err != nil {
   364  		m.httpError = err
   365  		return false, nil
   366  	}
   367  
   368  	m.httpError = nil
   369  	m.actualStatus = response.StatusCode
   370  	return response.StatusCode == m.expectedStatus, nil
   371  }
   372  
   373  func (m *statusMatcher) FailureMessage(actual interface{}) string {
   374  	if m.httpError != nil {
   375  		return fmt.Sprintf("Expected http request to have status %d but got error: %s", m.expectedStatus, m.httpError.Error())
   376  	}
   377  
   378  	return fmt.Sprintf("Expected http status code to be %d but was %d", m.expectedStatus, m.actualStatus)
   379  }
   380  
   381  func (m *statusMatcher) NegatedFailureMessage(actual interface{}) string {
   382  	if m.httpError != nil {
   383  		return fmt.Sprintf("Expected http request to have status %d, but got error: %s", m.expectedStatus, m.httpError.Error())
   384  	}
   385  
   386  	return fmt.Sprintf("Expected http status code not to be %d", m.expectedStatus)
   387  }
   388  
   389  func createSmallRootfs() string {
   390  	rootfs := os.Getenv("GARDEN_PREEXISTING_USERS_TEST_ROOTFS")
   391  	if rootfs == "" {
   392  		Skip("pre-existing users rootfs not found")
   393  	}
   394  
   395  	rootfspath, err := ioutil.TempDir("", "rootfs-cache-invalidation")
   396  	Expect(err).NotTo(HaveOccurred())
   397  	cmd := exec.Command("cp", "-rf", rootfs, rootfspath)
   398  	cmd.Stdout = GinkgoWriter
   399  	cmd.Stderr = GinkgoWriter
   400  	Expect(cmd.Run()).To(Succeed())
   401  	return filepath.Join(rootfspath, filepath.Base(rootfs))
   402  }