diff --git a/test/extended/images/layers.go b/test/extended/images/layers.go new file mode 100644 index 000000000000..c7ae6df67ea6 --- /dev/null +++ b/test/extended/images/layers.go @@ -0,0 +1,168 @@ +package images + +import ( + "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.NewCLI("image-layers", exutil.KubeConfigPath()) + + g.AfterEach(func() { + if g.CurrentGinkgoTestDescription().Failed { + exutil.DumpPodLogsStartingWith("", oc) + } + }) + + g.It("should return layers from tagged images", func() { + 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)) + + // TODO: we may race here with the cache, if this is a problem, loop + g.By("verifying that layers for imported images are correct") + layers, err := client.ImageStreams(oc.Namespace()).Layers("1") + o.Expect(err).NotTo(o.HaveOccurred()) + var busyboxLayers []string + var busyboxDigest string + for _, image := range isi.Status.Images { + l, ok := layers.Layers[image.Image.Name] + o.Expect(ok).To(o.BeTrue()) + o.Expect(len(l)).To(o.BeNumerically(">", 0)) + for _, layerID := range l { + o.Expect(layers.Blobs).To(o.HaveKey(layerID)) + o.Expect(layers.Blobs[layerID].MediaType).NotTo(o.BeEmpty()) + } + if image.Tag == "busybox" { + busyboxLayers = l + busyboxDigest = image.Image.Name + } + o.Expect(layers.Manifests).To(o.HaveKey(image.Image.Name)) + } + + _, 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.Layers).To(o.BeEmpty()) + o.Expect(layers.Blobs).To(o.BeEmpty()) + o.Expect(layers.Manifests).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 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: "busybox:latest"}, + }, + }, + Output: buildapi.BuildOutput{ + To: &kapi.ObjectReference{Kind: "ImageStreamTag", Name: "output:latest"}, + }, + }, + }, + }) + o.Expect(err).NotTo(o.HaveOccurred()) + + newNamespace := oc.CreateProject() + + 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.Layers).To(o.HaveKey(to.ImageDigest)) + builtImageLayers := layers.Layers[to.ImageDigest] + o.Expect(len(builtImageLayers)).To(o.Equal(len(busyboxLayers) + 1)) + for i := range busyboxLayers { + o.Expect(busyboxLayers[i]).To(o.Equal(builtImageLayers[i])) + } + o.Expect(layers.Layers).To(o.HaveKey(busyboxDigest)) + o.Expect(layers.Layers[busyboxDigest]).To(o.Equal(busyboxLayers)) + + g.By("tagging the built image into another namespace") + _, err = client.ImageStreamTags(newNamespace).Create(&imageapi.ImageStreamTag{ + ObjectMeta: metav1.ObjectMeta{ + Name: "output", + }, + 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.Layers).To(o.HaveKey(to.ImageDigest)) + o.Expect(layers.Layers[to.ImageDigest]).To(o.Equal(builtImageLayers)) + }) +}) diff --git a/test/extended/util/cli.go b/test/extended/util/cli.go index a95140439ce1..d0411915102e 100644 --- a/test/extended/util/cli.go +++ b/test/extended/util/cli.go @@ -179,6 +179,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 len(c.configPath) > 0 { @@ -201,11 +227,7 @@ func (c *CLI) Verbose() *CLI { } 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) } @@ -213,11 +235,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) } @@ -225,11 +243,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) } @@ -237,11 +251,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) } @@ -249,11 +259,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) } @@ -261,11 +267,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) } @@ -275,11 +277,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) } @@ -287,11 +285,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) } @@ -299,11 +293,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) } @@ -311,11 +301,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) } @@ -323,11 +309,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) } @@ -335,11 +317,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) } @@ -347,11 +325,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) } @@ -359,11 +333,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) } @@ -372,11 +342,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) } @@ -384,11 +350,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) } @@ -396,11 +358,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) } @@ -409,20 +367,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. @@ -435,6 +385,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 {