mirror of
https://github.com/genuinetools/reg.git
synced 2024-09-28 11:46:20 -04:00
57e5dcd240
Signed-off-by: Jess Frazelle <me@jessfraz.com>
260 lines
5.7 KiB
Go
260 lines
5.7 KiB
Go
package testutils
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"time"
|
|
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/api/types/container"
|
|
"github.com/docker/docker/client"
|
|
"github.com/docker/docker/pkg/jsonmessage"
|
|
"github.com/docker/docker/pkg/term"
|
|
)
|
|
|
|
const (
|
|
registryConfig = `version: 0.1
|
|
log:
|
|
level: debug
|
|
formatter: text
|
|
fields:
|
|
service: registry
|
|
storage:
|
|
filesystem:
|
|
rootdirectory: /var/lib/registry
|
|
http:
|
|
addr: 0.0.0.0:5000
|
|
headers:
|
|
X-Content-Type-Options: [nosniff]
|
|
host: https://localhost:5000
|
|
tls:
|
|
certificate: /etc/docker/registry/ssl/cert.pem
|
|
key: /etc/docker/registry/ssl/key.pem`
|
|
|
|
// admin:testing
|
|
htpasswd = "admin:$apr1$2a7OBK4C$pZEqDfaxN3Qaywsi5hMKt1\n"
|
|
)
|
|
|
|
// StartRegistry starts a new registry container.
|
|
func StartRegistry(dcli *client.Client) (string, string, error) {
|
|
image := "registry:2"
|
|
|
|
hostConfig, err := createRegistryConfig()
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
|
|
hostHtpasswd, err := createRegistryHtpasswd()
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
|
|
if err := pullDockerImage(dcli, image); err != nil {
|
|
return "", "", err
|
|
}
|
|
|
|
_, filename, _, ok := runtime.Caller(0)
|
|
if !ok {
|
|
return "", "", errors.New("No caller information")
|
|
}
|
|
|
|
fmt.Println(filename)
|
|
|
|
r, err := dcli.ContainerCreate(
|
|
context.Background(),
|
|
&container.Config{
|
|
Image: image,
|
|
/*ExposedPorts: map[nat.Port]struct{}{
|
|
"5000": {},
|
|
},*/
|
|
},
|
|
&container.HostConfig{
|
|
/* PortBindings: map[nat.Port][]nat.PortBinding{
|
|
"5000": []nat.PortBinding{{
|
|
HostIP: "0.0.0.0",
|
|
HostPort: "5000",
|
|
}},
|
|
},*/
|
|
NetworkMode: "host",
|
|
Binds: []string{
|
|
hostConfig + ":" + "/etc/docker/registry/config.yml" + ":ro",
|
|
hostHtpasswd + ":" + "/etc/docker/registry/htpasswd" + ":ro",
|
|
filepath.Join(filepath.Dir(filename), "snakeoil") + ":" + "/etc/docker/registry/ssl" + ":ro",
|
|
},
|
|
},
|
|
nil, "")
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
|
|
// start the container
|
|
if err := dcli.ContainerStart(context.Background(), r.ID, types.ContainerStartOptions{}); err != nil {
|
|
return r.ID, "", err
|
|
}
|
|
|
|
// get the container's IP
|
|
/*info, err := dcli.ContainerInspect(context.Background(), r.ID)
|
|
if err != nil {
|
|
return r.ID, "", err
|
|
}*/
|
|
port := ":5000"
|
|
// addr := "http://" + info.NetworkSettings.IPAddress + port
|
|
addr := "https://localhost" + port
|
|
|
|
// waitForConn(info.NetworkSettings.IPAddress + port)
|
|
waitForConn("localhost" + port)
|
|
|
|
if err := prefillRegistry(dcli, "localhost"+port); err != nil {
|
|
return r.ID, addr, err
|
|
}
|
|
|
|
return r.ID, addr, nil
|
|
}
|
|
|
|
// RemoveContainer removes with force a container by it's container ID.
|
|
func RemoveContainer(dcli *client.Client, ctrID string) error {
|
|
if err := dcli.ContainerRemove(context.Background(), ctrID,
|
|
types.ContainerRemoveOptions{
|
|
RemoveVolumes: true,
|
|
Force: true,
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// prefillRegistry adds images to a registry.
|
|
func prefillRegistry(dcli *client.Client, addr string) error {
|
|
image := "alpine:latest"
|
|
|
|
if err := pullDockerImage(dcli, image); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := dcli.ImageTag(context.Background(), image, addr+"/"+image); err != nil {
|
|
return err
|
|
}
|
|
|
|
auth, err := constructRegistryAuth("admin", "testing")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := dcli.ImagePush(context.Background(), addr+"/"+image, types.ImagePushOptions{
|
|
RegistryAuth: auth,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Close()
|
|
|
|
fd, isTerm := term.GetFdInfo(os.Stdout)
|
|
|
|
return jsonmessage.DisplayJSONMessagesStream(resp, os.Stdout, fd, isTerm, nil)
|
|
}
|
|
|
|
func pullDockerImage(dcli *client.Client, image string) error {
|
|
exists, err := imageExists(dcli, image)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if exists {
|
|
return nil
|
|
}
|
|
|
|
resp, err := dcli.ImagePull(context.Background(), image, types.ImagePullOptions{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Close()
|
|
|
|
fd, isTerm := term.GetFdInfo(os.Stdout)
|
|
|
|
return jsonmessage.DisplayJSONMessagesStream(resp, os.Stdout, fd, isTerm, nil)
|
|
}
|
|
|
|
func imageExists(dcli *client.Client, image string) (bool, error) {
|
|
_, _, err := dcli.ImageInspectWithRaw(context.Background(), image)
|
|
if err == nil {
|
|
return true, nil
|
|
}
|
|
|
|
if client.IsErrImageNotFound(err) {
|
|
return false, nil
|
|
}
|
|
|
|
return false, err
|
|
}
|
|
|
|
func createRegistryConfig() (string, error) {
|
|
tmpfile, err := ioutil.TempFile("", "registry")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if _, err := tmpfile.WriteString(registryConfig); err != nil {
|
|
return "", err
|
|
}
|
|
if err := tmpfile.Close(); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return tmpfile.Name(), nil
|
|
}
|
|
|
|
func createRegistryHtpasswd() (string, error) {
|
|
tmpfile, err := ioutil.TempFile("", "registry-htpasswd")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if _, err := tmpfile.WriteString(htpasswd); err != nil {
|
|
return "", err
|
|
}
|
|
if err := tmpfile.Close(); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return tmpfile.Name(), nil
|
|
}
|
|
|
|
// waitForConn takes a tcp addr and waits until it is reachable
|
|
func waitForConn(addr string) {
|
|
n := 0
|
|
max := 10
|
|
for n < max {
|
|
if _, err := net.Dial("tcp", addr); err != nil {
|
|
fmt.Printf("try number %d to dial %s: %v\n", n, addr, err)
|
|
n++
|
|
if n != max {
|
|
fmt.Println("sleeping for 1 second then will try again...")
|
|
time.Sleep(time.Second)
|
|
} else {
|
|
fmt.Printf("[WHOOPS]: maximum retries for %s exceeded\n", addr)
|
|
}
|
|
continue
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// constructRegistryAuth serializes the auth configuration as JSON base64 payload.
|
|
func constructRegistryAuth(identity, secret string) (string, error) {
|
|
buf, err := json.Marshal(types.AuthConfig{Username: identity, Password: secret})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return base64.URLEncoding.EncodeToString(buf), nil
|
|
}
|