diff --git a/test/extended/images/layers.go b/test/extended/images/layers.go new file mode 100644 index 000000000000..c6dc9602b0d8 --- /dev/null +++ b/test/extended/images/layers.go @@ -0,0 +1,181 @@ +package images + +import ( + "fmt" + "time" + + g "github.com/onsi/ginkgo" + o "github.com/onsi/gomega" + + kapi "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + + buildapi "github.com/openshift/api/build/v1" + imageapi "github.com/openshift/api/image/v1" + buildclientset "github.com/openshift/client-go/build/clientset/versioned" + imageclientset "github.com/openshift/client-go/image/clientset/versioned" + exutil "github.com/openshift/origin/test/extended/util" +) + +var _ = g.Describe("[Feature:ImageLayers] Image layer subresource", func() { + defer g.GinkgoRecover() + var oc *exutil.CLI + var ns []string + + g.AfterEach(func() { + if g.CurrentGinkgoTestDescription().Failed { + for _, s := range ns { + exutil.DumpPodLogsStartingWithInNamespace("", s, oc) + } + } + }) + + oc = exutil.NewCLI("image-layers", exutil.KubeConfigPath()) + + g.It("should return layers from tagged images", func() { + ns = []string{oc.Namespace()} + client := imageclientset.NewForConfigOrDie(oc.UserConfig()).Image() + isi, err := client.ImageStreamImports(oc.Namespace()).Create(&imageapi.ImageStreamImport{ + ObjectMeta: metav1.ObjectMeta{ + Name: "1", + }, + Spec: imageapi.ImageStreamImportSpec{ + Import: true, + Images: []imageapi.ImageImportSpec{ + { + From: kapi.ObjectReference{Kind: "DockerImage", Name: "busybox:latest"}, + To: &kapi.LocalObjectReference{Name: "busybox"}, + }, + { + From: kapi.ObjectReference{Kind: "DockerImage", Name: "mysql:latest"}, + To: &kapi.LocalObjectReference{Name: "mysql"}, + }, + }, + }, + }) + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(isi.Status.Images).To(o.HaveLen(2)) + for _, image := range isi.Status.Images { + o.Expect(image.Image).ToNot(o.BeNil(), fmt.Sprintf("image %s %#v", image.Tag, image.Status)) + } + + // TODO: we may race here with the cache, if this is a problem, loop + g.By("verifying that layers for imported images are correct") + var busyboxLayers []string + for i := 0; ; i++ { + layers, err := client.ImageStreams(oc.Namespace()).Layers("1") + o.Expect(err).NotTo(o.HaveOccurred()) + for i, image := range isi.Status.Images { + l, ok := layers.Images[image.Image.Name] + o.Expect(ok).To(o.BeTrue()) + o.Expect(len(l.Layers)).To(o.BeNumerically(">", 0)) + o.Expect(l.Manifest).ToNot(o.BeNil()) + for _, layerID := range l.Layers { + o.Expect(layers.Blobs).To(o.HaveKey(layerID)) + o.Expect(layers.Blobs[layerID].MediaType).NotTo(o.BeEmpty()) + } + if i == 0 { + busyboxLayers = l.Layers + } + } + if len(busyboxLayers) > 0 { + break + } + time.Sleep(time.Second) + o.Expect(i).To(o.BeNumerically("<", 10), "Timed out waiting for layers to have expected data, got\n%#v\n%#v", layers, isi.Status.Images) + } + + _, err = client.ImageStreams(oc.Namespace()).Create(&imageapi.ImageStream{ + ObjectMeta: metav1.ObjectMeta{ + Name: "output", + }, + }) + o.Expect(err).NotTo(o.HaveOccurred()) + + layers, err := client.ImageStreams(oc.Namespace()).Layers("output") + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(layers.Images).To(o.BeEmpty()) + o.Expect(layers.Blobs).To(o.BeEmpty()) + + _, err = client.ImageStreams(oc.Namespace()).Layers("doesnotexist") + o.Expect(err).To(o.HaveOccurred()) + o.Expect(errors.IsNotFound(err)).To(o.BeTrue()) + + dockerfile := ` +FROM a +RUN mkdir -p /var/lib && echo "a" > /var/lib/file +` + + g.By("running a build based on our tagged layer") + buildClient := buildclientset.NewForConfigOrDie(oc.UserConfig()).Build() + _, err = buildClient.Builds(oc.Namespace()).Create(&buildapi.Build{ + ObjectMeta: metav1.ObjectMeta{ + Name: "output", + }, + Spec: buildapi.BuildSpec{ + CommonSpec: buildapi.CommonSpec{ + Source: buildapi.BuildSource{ + Dockerfile: &dockerfile, + }, + Strategy: buildapi.BuildStrategy{ + DockerStrategy: &buildapi.DockerBuildStrategy{ + From: &kapi.ObjectReference{Kind: "ImageStreamTag", Name: "1:busybox"}, + }, + }, + Output: buildapi.BuildOutput{ + To: &kapi.ObjectReference{Kind: "ImageStreamTag", Name: "output:latest"}, + }, + }, + }, + }) + o.Expect(err).NotTo(o.HaveOccurred()) + + newNamespace := oc.CreateProject() + ns = append(ns) + + g.By("waiting for the build to finish") + var lastBuild *buildapi.Build + err = wait.Poll(time.Second, time.Minute, func() (bool, error) { + build, err := buildClient.Builds(oc.Namespace()).Get("output", metav1.GetOptions{}) + if err != nil { + return false, err + } + o.Expect(build.Status.Phase).NotTo(o.Or(o.Equal(buildapi.BuildPhaseFailed), o.Equal(buildapi.BuildPhaseError), o.Equal(buildapi.BuildPhaseCancelled))) + lastBuild = build + return build.Status.Phase == buildapi.BuildPhaseComplete, nil + }) + o.Expect(err).NotTo(o.HaveOccurred()) + + g.By("checking the layers for the built image") + layers, err = client.ImageStreams(oc.Namespace()).Layers("output") + o.Expect(err).NotTo(o.HaveOccurred()) + to := lastBuild.Status.Output.To + o.Expect(to).NotTo(o.BeNil()) + o.Expect(layers.Images).To(o.HaveKey(to.ImageDigest)) + builtImageLayers := layers.Images[to.ImageDigest] + o.Expect(len(builtImageLayers.Layers)).To(o.Equal(len(busyboxLayers)+1), fmt.Sprintf("%#v", layers.Images)) + for i := range busyboxLayers { + o.Expect(busyboxLayers[i]).To(o.Equal(builtImageLayers.Layers[i])) + } + + g.By("tagging the built image into another namespace") + _, err = client.ImageStreamTags(newNamespace).Create(&imageapi.ImageStreamTag{ + ObjectMeta: metav1.ObjectMeta{ + Name: "output:latest", + }, + Tag: &imageapi.TagReference{ + Name: "copied", + From: &kapi.ObjectReference{Kind: "ImageStreamTag", Namespace: oc.Namespace(), Name: "output:latest"}, + }, + }) + o.Expect(err).NotTo(o.HaveOccurred()) + + g.By("checking that the image shows up in the other namespace") + layers, err = client.ImageStreams(newNamespace).Layers("output") + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(layers.Images).To(o.HaveKey(to.ImageDigest)) + o.Expect(layers.Images[to.ImageDigest]).To(o.Equal(builtImageLayers)) + }) +}) diff --git a/test/extended/util/cli.go b/test/extended/util/cli.go index 3f5aa7074d84..28b02f40cf1f 100644 --- a/test/extended/util/cli.go +++ b/test/extended/util/cli.go @@ -182,6 +182,32 @@ func (c *CLI) SetupProject() { o.Expect(err).NotTo(o.HaveOccurred()) } +// SetupProject creates a new project and assign a random user to the project. +// All resources will be then created within this project. +func (c *CLI) CreateProject() string { + newNamespace := names.SimpleNameGenerator.GenerateName(fmt.Sprintf("e2e-test-%s-", c.kubeFramework.BaseName)) + e2e.Logf("Creating project %q", newNamespace) + _, err := c.ProjectClient().Project().ProjectRequests().Create(&projectapi.ProjectRequest{ + ObjectMeta: metav1.ObjectMeta{Name: newNamespace}, + }) + o.Expect(err).NotTo(o.HaveOccurred()) + + // TODO: remove when https://github.com/kubernetes/kubernetes/pull/62606 merges and is in origin + c.namespacesToDelete = append(c.namespacesToDelete, newNamespace) + + e2e.Logf("Waiting on permissions in project %q ...", newNamespace) + err = WaitForSelfSAR(1*time.Second, 60*time.Second, c.KubeClient(), authorizationapiv1.SelfSubjectAccessReviewSpec{ + ResourceAttributes: &authorizationapiv1.ResourceAttributes{ + Namespace: newNamespace, + Verb: "create", + Group: "", + Resource: "pods", + }, + }) + o.Expect(err).NotTo(o.HaveOccurred()) + return newNamespace +} + // TeardownProject removes projects created by this test. func (c *CLI) TeardownProject() { if g.CurrentGinkgoTestDescription().Failed && e2e.TestContext.DumpLogsOnFailure { @@ -214,11 +240,7 @@ func (c *CLI) RESTMapper() meta.RESTMapper { } func (c *CLI) AppsClient() appsclientset.Interface { - clientConfig, err := configapi.GetClientConfig(c.configPath, nil) - if err != nil { - FatalErr(err) - } - client, err := appsclientset.NewForConfig(clientConfig) + client, err := appsclientset.NewForConfig(c.UserConfig()) if err != nil { FatalErr(err) } @@ -226,11 +248,7 @@ func (c *CLI) AppsClient() appsclientset.Interface { } func (c *CLI) AuthorizationClient() authorizationclientset.Interface { - clientConfig, err := configapi.GetClientConfig(c.configPath, nil) - if err != nil { - FatalErr(err) - } - client, err := authorizationclientset.NewForConfig(clientConfig) + client, err := authorizationclientset.NewForConfig(c.UserConfig()) if err != nil { FatalErr(err) } @@ -238,11 +256,7 @@ func (c *CLI) AuthorizationClient() authorizationclientset.Interface { } func (c *CLI) BuildClient() buildclientset.Interface { - clientConfig, err := configapi.GetClientConfig(c.configPath, nil) - if err != nil { - FatalErr(err) - } - client, err := buildclientset.NewForConfig(clientConfig) + client, err := buildclientset.NewForConfig(c.UserConfig()) if err != nil { FatalErr(err) } @@ -250,11 +264,7 @@ func (c *CLI) BuildClient() buildclientset.Interface { } func (c *CLI) ImageClient() imageclientset.Interface { - clientConfig, err := configapi.GetClientConfig(c.configPath, nil) - if err != nil { - FatalErr(err) - } - client, err := imageclientset.NewForConfig(clientConfig) + client, err := imageclientset.NewForConfig(c.UserConfig()) if err != nil { FatalErr(err) } @@ -262,11 +272,7 @@ func (c *CLI) ImageClient() imageclientset.Interface { } func (c *CLI) ProjectClient() projectclientset.Interface { - clientConfig, err := configapi.GetClientConfig(c.configPath, nil) - if err != nil { - FatalErr(err) - } - client, err := projectclientset.NewForConfig(clientConfig) + client, err := projectclientset.NewForConfig(c.UserConfig()) if err != nil { FatalErr(err) } @@ -274,11 +280,7 @@ func (c *CLI) ProjectClient() projectclientset.Interface { } func (c *CLI) RouteClient() routeclientset.Interface { - clientConfig, err := configapi.GetClientConfig(c.configPath, nil) - if err != nil { - FatalErr(err) - } - client, err := routeclientset.NewForConfig(clientConfig) + client, err := routeclientset.NewForConfig(c.UserConfig()) if err != nil { FatalErr(err) } @@ -288,11 +290,7 @@ func (c *CLI) RouteClient() routeclientset.Interface { // Client provides an OpenShift client for the current user. If the user is not // set, then it provides client for the cluster admin user func (c *CLI) TemplateClient() templateclientset.Interface { - clientConfig, err := configapi.GetClientConfig(c.configPath, nil) - if err != nil { - FatalErr(err) - } - client, err := templateclientset.NewForConfig(clientConfig) + client, err := templateclientset.NewForConfig(c.UserConfig()) if err != nil { FatalErr(err) } @@ -300,11 +298,7 @@ func (c *CLI) TemplateClient() templateclientset.Interface { } func (c *CLI) UserClient() userclientset.Interface { - clientConfig, err := configapi.GetClientConfig(c.configPath, nil) - if err != nil { - FatalErr(err) - } - client, err := userclientset.NewForConfig(clientConfig) + client, err := userclientset.NewForConfig(c.UserConfig()) if err != nil { FatalErr(err) } @@ -312,11 +306,7 @@ func (c *CLI) UserClient() userclientset.Interface { } func (c *CLI) AdminAppsClient() appsclientset.Interface { - clientConfig, err := configapi.GetClientConfig(c.adminConfigPath, nil) - if err != nil { - FatalErr(err) - } - client, err := appsclientset.NewForConfig(clientConfig) + client, err := appsclientset.NewForConfig(c.AdminConfig()) if err != nil { FatalErr(err) } @@ -324,11 +314,7 @@ func (c *CLI) AdminAppsClient() appsclientset.Interface { } func (c *CLI) AdminAuthorizationClient() authorizationclientset.Interface { - clientConfig, err := configapi.GetClientConfig(c.adminConfigPath, nil) - if err != nil { - FatalErr(err) - } - client, err := authorizationclientset.NewForConfig(clientConfig) + client, err := authorizationclientset.NewForConfig(c.AdminConfig()) if err != nil { FatalErr(err) } @@ -336,11 +322,7 @@ func (c *CLI) AdminAuthorizationClient() authorizationclientset.Interface { } func (c *CLI) AdminBuildClient() buildclientset.Interface { - clientConfig, err := configapi.GetClientConfig(c.adminConfigPath, nil) - if err != nil { - FatalErr(err) - } - client, err := buildclientset.NewForConfig(clientConfig) + client, err := buildclientset.NewForConfig(c.AdminConfig()) if err != nil { FatalErr(err) } @@ -348,11 +330,7 @@ func (c *CLI) AdminBuildClient() buildclientset.Interface { } func (c *CLI) AdminImageClient() imageclientset.Interface { - clientConfig, err := configapi.GetClientConfig(c.adminConfigPath, nil) - if err != nil { - FatalErr(err) - } - client, err := imageclientset.NewForConfig(clientConfig) + client, err := imageclientset.NewForConfig(c.AdminConfig()) if err != nil { FatalErr(err) } @@ -360,11 +338,7 @@ func (c *CLI) AdminImageClient() imageclientset.Interface { } func (c *CLI) AdminProjectClient() projectclientset.Interface { - clientConfig, err := configapi.GetClientConfig(c.adminConfigPath, nil) - if err != nil { - FatalErr(err) - } - client, err := projectclientset.NewForConfig(clientConfig) + client, err := projectclientset.NewForConfig(c.AdminConfig()) if err != nil { FatalErr(err) } @@ -372,11 +346,7 @@ func (c *CLI) AdminProjectClient() projectclientset.Interface { } func (c *CLI) AdminRouteClient() routeclientset.Interface { - clientConfig, err := configapi.GetClientConfig(c.adminConfigPath, nil) - if err != nil { - FatalErr(err) - } - client, err := routeclientset.NewForConfig(clientConfig) + client, err := routeclientset.NewForConfig(c.AdminConfig()) if err != nil { FatalErr(err) } @@ -385,11 +355,7 @@ func (c *CLI) AdminRouteClient() routeclientset.Interface { // AdminClient provides an OpenShift client for the cluster admin user. func (c *CLI) AdminTemplateClient() templateclientset.Interface { - clientConfig, err := configapi.GetClientConfig(c.adminConfigPath, nil) - if err != nil { - FatalErr(err) - } - client, err := templateclientset.NewForConfig(clientConfig) + client, err := templateclientset.NewForConfig(c.AdminConfig()) if err != nil { FatalErr(err) } @@ -397,11 +363,7 @@ func (c *CLI) AdminTemplateClient() templateclientset.Interface { } func (c *CLI) AdminUserClient() userclientset.Interface { - clientConfig, err := configapi.GetClientConfig(c.adminConfigPath, nil) - if err != nil { - FatalErr(err) - } - client, err := userclientset.NewForConfig(clientConfig) + client, err := userclientset.NewForConfig(c.AdminConfig()) if err != nil { FatalErr(err) } @@ -409,11 +371,7 @@ func (c *CLI) AdminUserClient() userclientset.Interface { } func (c *CLI) AdminSecurityClient() securityclientset.Interface { - clientConfig, err := configapi.GetClientConfig(c.adminConfigPath, nil) - if err != nil { - FatalErr(err) - } - client, err := securityclientset.NewForConfig(clientConfig) + client, err := securityclientset.NewForConfig(c.AdminConfig()) if err != nil { FatalErr(err) } @@ -422,20 +380,12 @@ func (c *CLI) AdminSecurityClient() securityclientset.Interface { // KubeClient provides a Kubernetes client for the current namespace func (c *CLI) KubeClient() kclientset.Interface { - clientConfig, err := configapi.GetClientConfig(c.configPath, nil) - if err != nil { - FatalErr(err) - } - return kclientset.NewForConfigOrDie(clientConfig) + return kclientset.NewForConfigOrDie(c.UserConfig()) } // KubeClient provides a Kubernetes client for the current namespace func (c *CLI) InternalKubeClient() kinternalclientset.Interface { - clientConfig, err := configapi.GetClientConfig(c.configPath, nil) - if err != nil { - FatalErr(err) - } - return kinternalclientset.NewForConfigOrDie(clientConfig) + return kinternalclientset.NewForConfigOrDie(c.UserConfig()) } // AdminKubeClient provides a Kubernetes client for the cluster admin user. @@ -448,6 +398,14 @@ func (c *CLI) InternalAdminKubeClient() kinternalclientset.Interface { return kinternalclientset.NewForConfigOrDie(c.AdminConfig()) } +func (c *CLI) UserConfig() *restclient.Config { + clientConfig, err := configapi.GetClientConfig(c.configPath, nil) + if err != nil { + FatalErr(err) + } + return clientConfig +} + func (c *CLI) AdminConfig() *restclient.Config { clientConfig, err := configapi.GetClientConfig(c.adminConfigPath, nil) if err != nil {