reg/testutils/testutils.go
Jess Frazelle 57e5dcd240 inital testing
Signed-off-by: Jess Frazelle <me@jessfraz.com>
2017-04-10 14:20:23 +00:00

261 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
}