Test that the tags for a repo are scanned
In this commit I use the test registry implementation to check that the controller will scan the tags of an image. This needs a bit more scaffolding, since the test registry doesn't handle /tags/list.
This commit is contained in:
parent
31152814cc
commit
6ae6561b8b
|
@ -68,10 +68,6 @@ func (r *ImageRepositoryReconciler) Reconcile(req ctrl.Request) (ctrl.Result, er
|
|||
canonicalName := ref.Context().String()
|
||||
if imageRepo.Status.CanonicalImageName != canonicalName {
|
||||
imageRepo.Status.CanonicalImageName = canonicalName
|
||||
imageRepo.Status.LastError = ""
|
||||
if err := r.Status().Update(ctx, &imageRepo); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
|
|
@ -18,8 +18,12 @@ package controllers
|
|||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
"github.com/google/go-containerregistry/pkg/v1/random"
|
||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
@ -28,8 +32,14 @@ import (
|
|||
// +kubebuilder:scaffold:imports
|
||||
)
|
||||
|
||||
// https://github.com/google/go-containerregistry/blob/v0.1.1/pkg/registry/compatibility_test.go
|
||||
// has an example of loading a test registry with a random image.
|
||||
|
||||
var _ = Describe("ImageRepository controller", func() {
|
||||
It("expands the canonical image name", func() {
|
||||
// would be good to test this without needing to do the scanning, since
|
||||
// 1. better to not rely on external services being available
|
||||
// 2. probably going to want to have several test cases
|
||||
repo := imagev1alpha1.ImageRepository{
|
||||
Spec: imagev1alpha1.ImageRepositorySpec{
|
||||
Image: "alpine",
|
||||
|
@ -52,11 +62,51 @@ var _ = Describe("ImageRepository controller", func() {
|
|||
|
||||
var repoAfter imagev1alpha1.ImageRepository
|
||||
Eventually(func() bool {
|
||||
err = r.Get(context.Background(), imageRepoName, &repoAfter)
|
||||
err := r.Get(context.Background(), imageRepoName, &repoAfter)
|
||||
return err == nil && repoAfter.Status.CanonicalImageName != ""
|
||||
}, timeout, interval).Should(BeTrue())
|
||||
Expect(repoAfter.Name).To(Equal("alpine-image"))
|
||||
Expect(repoAfter.Namespace).To(Equal("default"))
|
||||
Expect(repoAfter.Status.CanonicalImageName).To(Equal("index.docker.io/library/alpine"))
|
||||
})
|
||||
|
||||
It("fetches the tags for an image", func() {
|
||||
versions := []string{"0.1.0", "0.1.1", "0.2.0", "1.0.0", "1.0.1", "1.0.2", "1.1.0-alpha"}
|
||||
registry := strings.TrimPrefix(registryServer.URL, "http://")
|
||||
imgRepo := registry + "/myimage"
|
||||
for _, tag := range versions {
|
||||
imgRef, err := name.NewTag(imgRepo + ":" + tag)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
img, err := random.Image(512, 1)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(remote.Write(imgRef, img)).To(Succeed())
|
||||
}
|
||||
|
||||
repo := imagev1alpha1.ImageRepository{
|
||||
Spec: imagev1alpha1.ImageRepositorySpec{
|
||||
Image: imgRepo,
|
||||
},
|
||||
}
|
||||
objectName := types.NamespacedName{
|
||||
Name: "random",
|
||||
Namespace: "default",
|
||||
}
|
||||
|
||||
repo.Name = objectName.Name
|
||||
repo.Namespace = objectName.Namespace
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
r := imageRepoReconciler
|
||||
Expect(r.Create(ctx, &repo)).To(Succeed())
|
||||
|
||||
var repoAfter imagev1alpha1.ImageRepository
|
||||
Eventually(func() bool {
|
||||
err := r.Get(context.Background(), objectName, &repoAfter)
|
||||
return err == nil && repoAfter.Status.CanonicalImageName != ""
|
||||
}, timeout, interval).Should(BeTrue())
|
||||
Expect(repoAfter.Status.CanonicalImageName).To(Equal(imgRepo))
|
||||
Expect(repoAfter.Status.LastScanResult.TagCount).To(Equal(len(versions)))
|
||||
})
|
||||
})
|
||||
|
|
|
@ -17,10 +17,15 @@ limitations under the License.
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/registry"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
|
@ -51,6 +56,7 @@ var k8sClient client.Client
|
|||
var k8sMgr ctrl.Manager
|
||||
var imageRepoReconciler *ImageRepositoryReconciler
|
||||
var testEnv *envtest.Environment
|
||||
var registryServer *httptest.Server
|
||||
|
||||
func TestAPIs(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
|
@ -101,6 +107,13 @@ var _ = BeforeSuite(func(done Done) {
|
|||
k8sClient = k8sMgr.GetClient()
|
||||
Expect(k8sClient).ToNot(BeNil())
|
||||
|
||||
// set up a local registry for testing scanning
|
||||
regHandler := registry.New()
|
||||
registryServer = httptest.NewServer(&tagListHandler{
|
||||
registryHandler: regHandler,
|
||||
imagetags: map[string][]string{},
|
||||
})
|
||||
|
||||
close(done)
|
||||
}, 60)
|
||||
|
||||
|
@ -108,4 +121,54 @@ var _ = AfterSuite(func() {
|
|||
By("tearing down the test environment")
|
||||
err := testEnv.Stop()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
registryServer.Close()
|
||||
})
|
||||
|
||||
// ---
|
||||
|
||||
// the go-containerregistry test regsitry implementation does not
|
||||
// serve /myimage/tags/list. Until it does, I'm adding this handler.
|
||||
// NB:
|
||||
// - assumes repo name is a single element
|
||||
// - assumes no overwriting tags
|
||||
|
||||
type tagListHandler struct {
|
||||
registryHandler http.Handler
|
||||
imagetags map[string][]string
|
||||
}
|
||||
|
||||
type tagListResult struct {
|
||||
Name string `json:"name"`
|
||||
Tags []string `json:"tags"`
|
||||
}
|
||||
|
||||
func (h *tagListHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// a tag list request has a path like: /v2/<repo>/tags/list
|
||||
if withoutTagsList := strings.TrimSuffix(r.URL.Path, "/tags/list"); r.Method == "GET" && withoutTagsList != r.URL.Path {
|
||||
repo := strings.TrimPrefix(withoutTagsList, "/v2/")
|
||||
if tags, ok := h.imagetags[repo]; ok {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
result := tagListResult{
|
||||
Name: repo,
|
||||
Tags: tags,
|
||||
}
|
||||
Expect(json.NewEncoder(w).Encode(result)).To(Succeed())
|
||||
println("Requested tags", repo, strings.Join(tags, ", "))
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// record the fact of a PUT to a tag; the path looks like: /v2/<repo>/manifests/<tag>
|
||||
h.registryHandler.ServeHTTP(w, r)
|
||||
if r.Method == "PUT" {
|
||||
pathElements := strings.Split(r.URL.Path, "/")
|
||||
if len(pathElements) == 5 && pathElements[1] == "v2" && pathElements[3] == "manifests" {
|
||||
repo, tag := pathElements[2], pathElements[4]
|
||||
println("Recording tag", repo, tag)
|
||||
h.imagetags[repo] = append(h.imagetags[repo], tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue