// BuildAndPushImageOfSizeWithDocker tries to build an image of wanted size and number of layers. It instructs // Docker daemon directly. Built image is stored as an image stream tag <name>:<tag>. If shouldSucceed is // false, a push is expected to fail with a denied error. Note the size is only approximate. Resulting image // size will be different depending on used compression algorithm and metadata overhead. func BuildAndPushImageOfSizeWithDocker( oc *exutil.CLI, dClient *dockerclient.Client, name, tag string, size uint64, numberOfLayers int, outSink io.Writer, shouldSucceed bool, ) (imageDigest string, err error) { registryURL, err := GetDockerRegistryURL(oc) if err != nil { return "", err } tempDir, err := ioutil.TempDir("", "name-build") if err != nil { return "", err } dataSize := calculateRoughDataSize(oc.Stdout(), size, numberOfLayers) lines := make([]string, numberOfLayers+1) lines[0] = "FROM scratch" for i := 1; i <= numberOfLayers; i++ { blobName := fmt.Sprintf("data%d", i) if err := createRandomBlob(path.Join(tempDir, blobName), dataSize); err != nil { return "", err } lines[i] = fmt.Sprintf("COPY %s /%s", blobName, blobName) } if err := ioutil.WriteFile(path.Join(tempDir, "Dockerfile"), []byte(strings.Join(lines, "\n")+"\n"), 0644); err != nil { return "", err } imageName := fmt.Sprintf("%s/%s/%s", registryURL, oc.Namespace(), name) taggedName := fmt.Sprintf("%s:%s", imageName, tag) err = dClient.BuildImage(dockerclient.BuildImageOptions{ Name: taggedName, RmTmpContainer: true, ForceRmTmpContainer: true, ContextDir: tempDir, OutputStream: outSink, }) if err != nil { return "", fmt.Errorf("failed to build %q image: %v", taggedName, err) } image, err := dClient.InspectImage(taggedName) if err != nil { return "", err } defer dClient.RemoveImageExtended(image.ID, dockerclient.RemoveImageOptions{Force: true}) digest := "" if len(image.RepoDigests) == 1 { digest = image.RepoDigests[0] } out, err := oc.Run("whoami").Args("-t").Output() if err != nil { return "", err } token := strings.TrimSpace(out) var buf bytes.Buffer err = dClient.PushImage(dockerclient.PushImageOptions{ Name: imageName, Tag: tag, Registry: registryURL, OutputStream: &buf, }, dockerclient.AuthConfiguration{ Username: "******", Password: token, Email: "*****@*****.**", ServerAddress: registryURL, }) out = buf.String() outSink.Write([]byte(out)) if shouldSucceed { if err != nil { return "", fmt.Errorf("Got unexpected push error: %v", err) } if len(digest) == 0 { outSink.Write([]byte("matching digest string\n")) match := rePushedImageDigest.FindStringSubmatch(out) if len(match) < 2 { return "", fmt.Errorf("Failed to parse digest") } digest = match[1] } return digest, nil } if err == nil { return "", fmt.Errorf("Push unexpectedly succeeded") } if !reExpectedDeniedError.MatchString(err.Error()) { return "", fmt.Errorf("Failed to match expected %q in: %q", reExpectedDeniedError.String(), err.Error()) } return "", nil }