Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
dmage committed Dec 1, 2020
1 parent d5c0aaf commit 0f79a14
Show file tree
Hide file tree
Showing 4 changed files with 383 additions and 279 deletions.
315 changes: 315 additions & 0 deletions pkg/testframework/WIP.go
@@ -0,0 +1,315 @@
package testframework

import (
"bytes"
"context"
"encoding/json"
"fmt"
"testing"
"time"

"golang.org/x/crypto/bcrypt"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
utilrand "k8s.io/apimachinery/pkg/util/rand"
"k8s.io/apimachinery/pkg/util/wait"
kubeclient "k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"

routev1 "github.com/openshift/api/route/v1"
routeclient "github.com/openshift/client-go/route/clientset/versioned"
)

type AuthConfig struct {
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
}

type DockerConfig struct {
Auths map[string]AuthConfig `json:"auths"`
}

func MakeDockerConfigSecret(name string, config *DockerConfig) (*corev1.Secret, error) {
buf, err := json.Marshal(config)
if err != nil {
return nil, err
}

return &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Data: map[string][]byte{
".dockerconfigjson": buf,
},
Type: corev1.SecretTypeDockerConfigJson,
}, nil
}

type CleanupFunc func()

func CreateEphemeralRegistry(t *testing.T, restConfig *rest.Config, namespace string, accounts map[string]string) (string, CleanupFunc) {
ctx := context.Background()

kubeClient, err := kubeclient.NewForConfig(restConfig)
if err != nil {
t.Fatalf("failed to create Kubernetes client: %s", err)
}

routeClient, err := routeclient.NewForConfig(restConfig)
if err != nil {
t.Fatalf("failed to create OpenShift route client: %s", err)
}

name := "ephemeral-registry-" + utilrand.String(5)

var cleaners []func()
cleanup := func() {
for _, c := range cleaners {
c()
}

var lastErr error
err = wait.Poll(time.Second, 30*time.Second, func() (done bool, err error) {
pod, err := kubeClient.CoreV1().Pods(namespace).Get(ctx, name, metav1.GetOptions{})
if errors.IsNotFound(err) {
return true, nil
}
if err != nil {
lastErr = err
t.Logf("unable to check if pod %s is deleted: %s", name, err)
}
lastErr = fmt.Errorf("pod %s is alive (deletionTimestamp: %s)", name, pod.DeletionTimestamp)
return false, nil
})
if err != nil {
t.Errorf("failed to delete ephemeral registry %s: %s", name, lastErr)
return
}
t.Logf("deleted ephemeral registry %s", name)
}

var volumes []corev1.Volume
var mounts []corev1.VolumeMount
var env []corev1.EnvVar
if accounts != nil {
var b bytes.Buffer
for user, password := range accounts {
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
t.Fatalf("failed to create bcrypt hash of password: %s", err)
}
fmt.Fprintf(&b, "%s:%s\n", user, hash)
}

secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Data: map[string][]byte{
"htpasswd": b.Bytes(),
},
}

_, err = kubeClient.CoreV1().Secrets(namespace).Create(ctx, secret, metav1.CreateOptions{})
if err != nil {
t.Fatalf("failed to create secret for ephemeral registry: %s", err)
}
cleaners = append(cleaners, func() {
err = kubeClient.CoreV1().Secrets(namespace).Delete(ctx, name, metav1.DeleteOptions{})
if err != nil {
t.Errorf("failed to delete secret %s: %s", name, err)
}
})

volumes = append(volumes, corev1.Volume{
Name: "auth",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: name,
},
},
})
mounts = append(mounts, corev1.VolumeMount{
Name: "auth",
MountPath: "/auth",
})
env = append(env, corev1.EnvVar{
Name: "REGISTRY_AUTH",
Value: "htpasswd",
})
env = append(env, corev1.EnvVar{
Name: "REGISTRY_AUTH_HTPASSWD_REALM",
Value: "Registry",
})
env = append(env, corev1.EnvVar{
Name: "REGISTRY_AUTH_HTPASSWD_PATH",
Value: "/auth/htpasswd",
})
}

pod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: map[string]string{
"name": name,
},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "registry",
Image: "docker.io/library/registry:2.7.1",
Ports: []corev1.ContainerPort{
{
ContainerPort: 5000,
},
},
Env: env,
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("10m"),
corev1.ResourceMemory: resource.MustParse("50Mi"),
},
},
VolumeMounts: mounts,
LivenessProbe: &corev1.Probe{
Handler: corev1.Handler{
HTTPGet: &corev1.HTTPGetAction{
Path: "/",
Port: intstr.FromInt(5000),
},
},
},
ReadinessProbe: &corev1.Probe{
Handler: corev1.Handler{
HTTPGet: &corev1.HTTPGetAction{
Path: "/",
Port: intstr.FromInt(5000),
},
},
},
TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError,
},
},
Volumes: volumes,
},
}

service := &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: corev1.ServiceSpec{
Ports: []corev1.ServicePort{
{
Port: 5000,
},
},
Selector: map[string]string{
"name": name,
},
},
}

route := &routev1.Route{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: routev1.RouteSpec{
To: routev1.RouteTargetReference{
Kind: "Service",
Name: name,
},
Port: &routev1.RoutePort{
TargetPort: intstr.FromInt(5000),
},
TLS: &routev1.TLSConfig{
Termination: routev1.TLSTerminationEdge,
},
},
}

_, err = kubeClient.CoreV1().Pods(namespace).Create(ctx, pod, metav1.CreateOptions{})
if err != nil {
t.Fatalf("failed to create ephemeral registry pod: %s", err)
}
cleaners = append(cleaners, func() {
err = kubeClient.CoreV1().Pods(namespace).Delete(ctx, name, metav1.DeleteOptions{})
if err != nil {
t.Errorf("failed to delete pod %s: %s", name, err)
}
})

_, err = kubeClient.CoreV1().Services(namespace).Create(ctx, service, metav1.CreateOptions{})
if err != nil {
t.Fatalf("failed to create service for ephemeral registry: %s", err)
}
cleaners = append(cleaners, func() {
err = kubeClient.CoreV1().Services(namespace).Delete(ctx, name, metav1.DeleteOptions{})
if err != nil {
t.Errorf("failed to delete service %s: %s", name, err)
}
})

_, err = routeClient.RouteV1().Routes(namespace).Create(ctx, route, metav1.CreateOptions{})
if err != nil {
t.Fatalf("failed to create route for ephemeral registry: %s", err)
}
cleaners = append(cleaners, func() {
err = routeClient.RouteV1().Routes(namespace).Delete(ctx, name, metav1.DeleteOptions{})
if err != nil {
t.Errorf("failed to delete route %s: %s", name, err)
}
})

var lastErr error
err = wait.Poll(time.Second, 30*time.Second, func() (done bool, err error) {
pod, err = kubeClient.CoreV1().Pods(namespace).Get(ctx, name, metav1.GetOptions{})
if err != nil {
lastErr = err
return false, nil
}
if pod.Status.Phase != "Running" {
lastErr = fmt.Errorf("pod phase is %s, want Running", pod.Status.Phase)
return false, nil
}
for _, c := range pod.Status.ContainerStatuses {
if !c.Ready {
lastErr = fmt.Errorf("container %s is not ready (restartCount: %d, state: %v)", c.Name, c.RestartCount, c.State)
return false, nil
}
}
return true, nil
})
if err != nil {
t.Fatalf("failed to wait until pod %s is ready: %v", name, lastErr)
}

var host string
err = wait.Poll(time.Second, 30*time.Second, func() (done bool, err error) {
route, err = routeClient.RouteV1().Routes(namespace).Get(ctx, name, metav1.GetOptions{})
if err != nil {
lastErr = err
return false, nil
}
for _, ingress := range route.Status.Ingress {
if len(ingress.Host) > 0 {
host = ingress.Host
return true, nil
}
}
lastErr = fmt.Errorf("route %s does not have ingress hosts", name)
return false, nil
})
if err != nil {
t.Fatalf("failed to wait until route %s is ready: %v", name, lastErr)
}

t.Logf("created ephemeral registry: %s (%s)", host, name)
return host, cleanup
}
51 changes: 50 additions & 1 deletion pkg/testframework/manifest.go
@@ -1,12 +1,16 @@
package testframework

import (
"context"
"encoding/json"
"fmt"
"net/http"

"github.com/docker/distribution"
"github.com/opencontainers/go-digest"
"github.com/pborman/uuid"

"github.com/openshift/image-registry/pkg/testutil"
)

type Schema2ImageData struct {
Expand All @@ -22,6 +26,20 @@ type Schema2ImageData struct {
}

func NewSchema2ImageData() (Schema2ImageData, error) {
/*
cfg := map[string]interface{}{
"rootfs": map[string]interface{}{
"diff_ids": make([]string, len(layers)),
},
"history": make([]struct{}, len(layers)),
}
configContent, err := json.Marshal(&cfg)
if err != nil {
return nil, fmt.Errorf("marshal image config: %w", err)
}
*/

data := Schema2ImageData{
ConfigMediaType: "application/vnd.docker.container.image.v1+json",
Config: []byte("{}"),
Expand Down Expand Up @@ -128,5 +146,36 @@ func PushImage(reference string, data Schema2ImageData) Repository(namespace st
repoName: repoName,
transport: transport,
}
}
func UnmarshalManifest(ctHeader string, p []byte) (Manifest, Descriptor, error) {
*/

func PushSchema2ImageData(ctx context.Context, repo distribution.Repository, tag string, data Schema2ImageData) (distribution.Manifest, error) {
layerDesc := distribution.Descriptor{
Digest: data.LayerDigest,
Size: int64(len(data.Layer)),
}

if err := testutil.UploadBlob(ctx, repo, layerDesc, data.Layer); err != nil {
return nil, fmt.Errorf("upload layer: %w", err)
}

configDesc := distribution.Descriptor{
Digest: data.ConfigDigest,
Size: int64(len(data.Config)),
}

if err := testutil.UploadBlob(ctx, repo, configDesc, data.Config); err != nil {
return nil, fmt.Errorf("upload image config: %w", err)
}

manifest, _, err := distribution.UnmarshalManifest(data.ManifestMediaType, data.Manifest)
if err != nil {
return manifest, fmt.Errorf("parse manifest: %w", err)
}

if err := testutil.UploadManifest(ctx, repo, tag, manifest); err != nil {
return manifest, fmt.Errorf("upload manifest: %w", err)
}

return manifest, nil
}

0 comments on commit 0f79a14

Please sign in to comment.