Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 13 additions & 68 deletions cmd/registry-replacer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import (
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"sync"

"github.com/sirupsen/logrus"
Expand All @@ -30,6 +28,7 @@ import (
"github.com/openshift/ci-tools/pkg/api"
"github.com/openshift/ci-tools/pkg/api/ocpbuilddata"
"github.com/openshift/ci-tools/pkg/config"
cidockerfile "github.com/openshift/ci-tools/pkg/dockerfile"
"github.com/openshift/ci-tools/pkg/github"
"github.com/openshift/ci-tools/pkg/load"
"github.com/openshift/ci-tools/pkg/registry"
Expand Down Expand Up @@ -296,9 +295,9 @@ func replacer(
continue
}
config.BaseImages[foundTag.String()] = api.ImageStreamTagReference{
Namespace: foundTag.org,
Name: foundTag.repo,
Tag: foundTag.tag,
Namespace: foundTag.Org,
Name: foundTag.Repo,
Tag: foundTag.Tag,
}
}

Expand Down Expand Up @@ -356,37 +355,17 @@ func replacer(
}
}

var registryRegex = regexp.MustCompile(`registry\.(|svc\.)ci\.openshift\.org/\S+`)

type orgRepoTag struct{ org, repo, tag string }

func (ort orgRepoTag) String() string {
return ort.org + "_" + ort.repo + "_" + ort.tag
}

func ensureReplacement(image *api.ProjectDirectoryImageBuildStepConfiguration, dockerfile []byte) ([]orgRepoTag, error) {
var toReplace []string
for _, line := range bytes.Split(dockerfile, []byte("\n")) {
if !bytes.Contains(line, []byte("FROM")) && !bytes.Contains(line, []byte("COPY")) && !bytes.Contains(line, []byte("copy")) {
continue
}
match := registryRegex.Find(line)
if match == nil {
continue
}

toReplace = append(toReplace, string(match))
}

var result []orgRepoTag
func ensureReplacement(image *api.ProjectDirectoryImageBuildStepConfiguration, dockerfile []byte) ([]cidockerfile.OrgRepoTag, error) {
toReplace := cidockerfile.ExtractRegistryReferences(dockerfile)
var result []cidockerfile.OrgRepoTag
for _, toReplace := range toReplace {
orgRepoTag, err := orgRepoTagFromPullString(toReplace)
orgRepoTag, err := cidockerfile.OrgRepoTagFromPullString(toReplace)
if err != nil {
return nil, fmt.Errorf("failed to parse string %s as pullspec: %w", toReplace, err)
}

// Assume ppl know what they are doing
if hasReplacementFor(image, toReplace) {
if cidockerfile.HasManualReplacementFor(image.Inputs, toReplace) {
continue
}

Expand All @@ -403,40 +382,6 @@ func ensureReplacement(image *api.ProjectDirectoryImageBuildStepConfiguration, d
return result, nil
}

func hasReplacementFor(image *api.ProjectDirectoryImageBuildStepConfiguration, target string) bool {
for _, input := range image.Inputs {
if sets.New[string](input.As...).Has(target) {
return true
}
}

return false
}

func orgRepoTagFromPullString(pullString string) (orgRepoTag, error) {
res := orgRepoTag{tag: "latest"}
slashSplit := strings.Split(pullString, "/")
n := len(slashSplit)

switch {
case n == 1:
res.org = "_"
res.repo = slashSplit[0]
case n >= 2:
res.org = slashSplit[n-2]
res.repo = slashSplit[n-1]
default:
return res, fmt.Errorf("pull string %q couldn't be parsed, got %d components", pullString, n)
}

if repoTag := strings.Split(res.repo, ":"); len(repoTag) == 2 {
res.repo = repoTag[0]
res.tag = repoTag[1]
}

return res, nil
}

func upsertPR(gc pgithub.Client, dir, githubUsername string, token []byte, selfApprove, pruneUnusedReplacements, ensureCorrectPromotionDockerfile bool) error {
if err := os.Chdir(dir); err != nil {
return fmt.Errorf("failed to chdir into %s: %w", dir, err)
Expand Down Expand Up @@ -584,11 +529,11 @@ func pruneUnusedReplacements(config *api.ReleaseBuildConfiguration, replacementC

func pruneOCPBuilderReplacements(config *api.ReleaseBuildConfiguration) error {
return pruneReplacements(config, func(asDirective string, imageKey string) (bool, error) {
orgRepoTag, err := orgRepoTagFromPullString(asDirective)
orgRepoTag, err := cidockerfile.OrgRepoTagFromPullString(asDirective)
if err != nil {
return false, fmt.Errorf("failed to extract org and tag from pull spec %s: %w", asDirective, err)
}
if orgRepoTag.org != "ocp" || orgRepoTag.repo != "builder" {
if orgRepoTag.Org != "ocp" || orgRepoTag.Repo != "builder" {
return true, nil
}

Expand All @@ -612,7 +557,7 @@ func pruneOCPBuilderReplacements(config *api.ReleaseBuildConfiguration) error {
}

// Fun special case: We set up a replacement for this ourselves to prevent direct references to api.ci
if imagestreamTagReference.Namespace == orgRepoTag.org && imagestreamTagReference.Name == orgRepoTag.repo && imagestreamTagReference.Tag == orgRepoTag.tag {
if imagestreamTagReference.Namespace == orgRepoTag.Org && imagestreamTagReference.Name == orgRepoTag.Repo && imagestreamTagReference.Tag == orgRepoTag.Tag {
return true, nil
}

Expand Down Expand Up @@ -705,7 +650,7 @@ func pruneUnusedBaseImages(config *api.ReleaseBuildConfiguration, resolvedConfig
pruneImage := func(images *map[string]api.ImageStreamTagReference, sourceImage string) error {
var keep bool
for candidate := range usedBaseImages {
orgRepoTag, err := orgRepoTagFromPullString(candidate)
orgRepoTag, err := cidockerfile.OrgRepoTagFromPullString(candidate)
if err != nil {
return fmt.Errorf("failed to parse string %s as pullspec: %w", candidate, err)
}
Expand Down
99 changes: 4 additions & 95 deletions cmd/registry-replacer/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import (
"github.com/google/go-cmp/cmp/cmpopts"

"k8s.io/apimachinery/pkg/util/sets"
utilpointer "k8s.io/utils/pointer"
"k8s.io/utils/ptr"

"github.com/openshift/ci-tools/pkg/api"
"github.com/openshift/ci-tools/pkg/api/ocpbuilddata"
"github.com/openshift/ci-tools/pkg/config"
cidockerfile "github.com/openshift/ci-tools/pkg/dockerfile"
"github.com/openshift/ci-tools/pkg/github"
"github.com/openshift/ci-tools/pkg/testhelper"
)
Expand Down Expand Up @@ -50,7 +51,7 @@ func TestReplacer(t *testing.T) {
{
name: "Use dockerfile_literal if present",
config: &api.ReleaseBuildConfiguration{
Images: []api.ProjectDirectoryImageBuildStepConfiguration{{ProjectDirectoryImageBuildInputs: api.ProjectDirectoryImageBuildInputs{DockerfileLiteral: utilpointer.String("FROM registry.svc.ci.openshift.org/org/repo:tag")}}},
Images: []api.ProjectDirectoryImageBuildStepConfiguration{{ProjectDirectoryImageBuildInputs: api.ProjectDirectoryImageBuildInputs{DockerfileLiteral: ptr.To("FROM registry.svc.ci.openshift.org/org/repo:tag")}}},
},
expectWrite: true,
},
Expand Down Expand Up @@ -947,102 +948,10 @@ func TestRegistryRegex(t *testing.T) {

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
actual := registryRegex.Find([]byte(tc.line))
actual := cidockerfile.RegistryRegex.Find([]byte(tc.line))
if diff := cmp.Diff(tc.expected, string(actual)); diff != "" {
t.Errorf("actual does not match expected, diff: %s", diff)
}
})
}
}

func TestOrgRepoTagFromPullString(t *testing.T) {
tests := []struct {
name string
pullString string
expected orgRepoTag
expectErr bool
}{
{
name: "single component (repo only)",
pullString: "redis",
expected: orgRepoTag{org: "_", repo: "redis", tag: "latest"},
},
{
name: "single component with tag",
pullString: "redis:6.0",
expected: orgRepoTag{org: "_", repo: "redis", tag: "6.0"},
},
{
name: "two components (org/repo)",
pullString: "library/redis",
expected: orgRepoTag{org: "library", repo: "redis", tag: "latest"},
},
{
name: "two components with tag",
pullString: "library/redis:6.0",
expected: orgRepoTag{org: "library", repo: "redis", tag: "6.0"},
},
{
name: "three components (registry/org/repo)",
pullString: "docker.io/library/redis",
expected: orgRepoTag{org: "library", repo: "redis", tag: "latest"},
},
{
name: "three components with tag",
pullString: "docker.io/library/redis:6.0",
expected: orgRepoTag{org: "library", repo: "redis", tag: "6.0"},
},
{
name: "four components (the failing case from the error)",
pullString: "quay.io/redhat-services-prod/openshift/boilerplate",
expected: orgRepoTag{org: "openshift", repo: "boilerplate", tag: "latest"},
},
{
name: "four components with tag",
pullString: "quay.io/redhat-services-prod/openshift/boilerplate:image-v7.4.0",
expected: orgRepoTag{org: "openshift", repo: "boilerplate", tag: "image-v7.4.0"},
},
{
name: "five components (deeply nested)",
pullString: "registry.com/team/project/subproject/service/image",
expected: orgRepoTag{org: "service", repo: "image", tag: "latest"},
},
{
name: "five components with tag",
pullString: "registry.com/team/project/subproject/service/image:v1.2.3",
expected: orgRepoTag{org: "service", repo: "image", tag: "v1.2.3"},
},
{
name: "registry.ci.openshift.org example",
pullString: "registry.ci.openshift.org/ocp/4.6:golang",
expected: orgRepoTag{org: "ocp", repo: "4.6", tag: "golang"},
},
{
name: "registry.svc.ci.openshift.org example",
pullString: "registry.svc.ci.openshift.org/ocp/builder:rhel-8-golang-1.15",
expected: orgRepoTag{org: "ocp", repo: "builder", tag: "rhel-8-golang-1.15"},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
result, err := orgRepoTagFromPullString(tc.pullString)

if tc.expectErr {
if err == nil {
t.Errorf("expected error but got none")
}
return
}

if err != nil {
t.Errorf("unexpected error: %v", err)
return
}

if diff := cmp.Diff(tc.expected, result, cmp.AllowUnexported(orgRepoTag{})); diff != "" {
t.Errorf("result does not match expected: %s", diff)
}
})
}
}
Loading