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
2 changes: 1 addition & 1 deletion cmd/registry-replacer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ func replacer(
}

func ensureReplacement(image *api.ProjectDirectoryImageBuildStepConfiguration, dockerfile []byte) ([]cidockerfile.OrgRepoTag, error) {
toReplace := cidockerfile.ExtractRegistryReferences(dockerfile, "")
toReplace := cidockerfile.ExtractRegistryReferences(dockerfile)
var result []cidockerfile.OrgRepoTag
for _, toReplace := range toReplace {
orgRepoTag, err := cidockerfile.OrgRepoTagFromPullString(toReplace)
Expand Down
2 changes: 1 addition & 1 deletion pkg/defaults/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -980,7 +980,7 @@ func readDockerfileForImage(image api.ProjectDirectoryImageBuildStepConfiguratio
// required InputImageTagStepConfiguration steps to import them, as well as an updated
// image configuration with the detected inputs added
func processDetectedBaseImages(baseImages map[string]api.ImageStreamTagReference, image api.ProjectDirectoryImageBuildStepConfiguration, details dockerfileDetails) ([]api.StepConfiguration, api.ProjectDirectoryImageBuildStepConfiguration) {
detectedBaseImages := dockerfile.DetectInputsFromDockerfile(details.content, image.Inputs, image.From)
detectedBaseImages := dockerfile.DetectInputsFromDockerfile(details.content, image.Inputs, image.From, baseImages)
if len(detectedBaseImages) == 0 {
return nil, image
}
Expand Down
17 changes: 1 addition & 16 deletions pkg/dockerfile/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@ func (ort OrgRepoTag) String() string {
}

// ExtractRegistryReferences finds all registry.ci.openshift.org and quay-proxy.ci.openshift.org references in the Dockerfile
func ExtractRegistryReferences(dockerfile []byte, from api.PipelineImageStreamTagReference) []string {
func ExtractRegistryReferences(dockerfile []byte) []string {
var refs []string
seen := sets.Set[string]{}
lastFromRef := ""

for _, line := range bytes.Split(dockerfile, []byte("\n")) {
upper := bytes.ToUpper(line)
Expand All @@ -40,25 +39,11 @@ func ExtractRegistryReferences(dockerfile []byte, from api.PipelineImageStreamTa
continue
}
ref := string(match)
if bytes.HasPrefix(upper, []byte("FROM")) {
lastFromRef = ref
}

if !seen.Has(ref) {
refs = append(refs, ref)
seen.Insert(ref)
}
}
if from != "" {
// If from is specified, remove the last detected FROM ref, it will be replaced
var newRefs []string
for _, ref := range refs {
if ref != lastFromRef {
newRefs = append(newRefs, ref)
}
}
refs = newRefs
}
return refs
}

Expand Down
29 changes: 24 additions & 5 deletions pkg/dockerfile/inputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,15 @@ import (
// DetectInputsFromDockerfile parses a Dockerfile and detects registry references that need to be added as base images
// Returns a map of base image names to ImageStreamTagReferences
// The ImageStreamTagReference.As field contains the original registry reference from the Dockerfile
func DetectInputsFromDockerfile(dockerfile []byte, existingInputs map[string]api.ImageBuildInputs, from api.PipelineImageStreamTagReference) map[string]api.ImageStreamTagReference {
registryRefs := ExtractRegistryReferences(dockerfile, from)
baseImages := make(map[string]api.ImageStreamTagReference)
func DetectInputsFromDockerfile(dockerfile []byte, existingInputs map[string]api.ImageBuildInputs, from api.PipelineImageStreamTagReference, baseImages map[string]api.ImageStreamTagReference) map[string]api.ImageStreamTagReference {
registryRefs := ExtractRegistryReferences(dockerfile)
detected := make(map[string]api.ImageStreamTagReference)

for _, ref := range registryRefs {
if from != "" && matchesFromBaseImage(ref, from, baseImages) {
logrus.WithField("reference", ref).WithField("from", from).Debug("Skipping Dockerfile input already provided by image from")
continue
}
if HasManualReplacementFor(existingInputs, ref) {
logrus.WithField("reference", ref).Debug("Skipping Dockerfile inputs detection: manual replacement exists")
continue
Expand All @@ -24,13 +28,28 @@ func DetectInputsFromDockerfile(dockerfile []byte, existingInputs map[string]api
continue
}
baseImageKey := orgRepoTag.String()
baseImages[baseImageKey] = api.ImageStreamTagReference{
detected[baseImageKey] = api.ImageStreamTagReference{
Namespace: orgRepoTag.Org,
Name: orgRepoTag.Repo,
Tag: orgRepoTag.Tag,
As: ref,
}
}

return baseImages
return detected
}

func matchesFromBaseImage(ref string, from api.PipelineImageStreamTagReference, baseImages map[string]api.ImageStreamTagReference) bool {
if from == "" || baseImages == nil {
return false
}
base, ok := baseImages[string(from)]
if !ok {
return false
}
orgRepoTag, err := OrgRepoTagFromPullString(ref)
if err != nil {
return false
}
return orgRepoTag.Org == base.Namespace && orgRepoTag.Repo == base.Name && orgRepoTag.Tag == base.Tag
}
49 changes: 39 additions & 10 deletions pkg/dockerfile/inputs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ func TestDetectInputsFromDockerfile(t *testing.T) {
name string
dockerfile string
existingInputs map[string]api.ImageBuildInputs
baseImages map[string]api.ImageStreamTagReference
expected map[string]api.ImageStreamTagReference
from api.PipelineImageStreamTagReference
}{
Expand Down Expand Up @@ -142,20 +143,26 @@ FROM registry.ci.openshift.org/ocp/4.19:base AS runtime
},
},
{
name: "from: is specified - should exclude the last and only detected FROM ref",
name: "from matches base image - skip duplicate",
dockerfile: `FROM registry.ci.openshift.org/ocp/4.19:base
RUN echo "hello"
`,
from: "src",
from: "src",
baseImages: map[string]api.ImageStreamTagReference{
"src": {Namespace: "ocp", Name: "4.19", Tag: "base"},
},
expected: map[string]api.ImageStreamTagReference{},
},
{
name: "from: is specified - should exclude the last detected FROM ref",
name: "from matches only its base image in multi-stage",
dockerfile: `FROM registry.ci.openshift.org/ocp/4.18:base AS builder
FROM registry.ci.openshift.org/ocp/4.19:base
RUN echo "hello"
`,
from: "src",
baseImages: map[string]api.ImageStreamTagReference{
"src": {Namespace: "ocp", Name: "4.19", Tag: "base"},
},
expected: map[string]api.ImageStreamTagReference{
"ocp_4.18_base": {
Namespace: "ocp",
Expand All @@ -166,33 +173,55 @@ RUN echo "hello"
},
},
{
name: "from: is specified - should exclude the last detected FROM ref with COPY",
name: "multi-stage cli with unrelated from",
dockerfile: `FROM registry.ci.openshift.org/ocp/4.14:cli AS cli
FROM quay.io/centos/centos:stream9
COPY --from=cli /usr/bin/oc /usr/bin/
`,
from: "stream9",
baseImages: map[string]api.ImageStreamTagReference{
"stream9": {Namespace: "openshift", Name: "centos", Tag: "stream9"},
},
expected: map[string]api.ImageStreamTagReference{
"ocp_4.14_cli": {
Namespace: "ocp",
Name: "4.14",
Tag: "cli",
As: "registry.ci.openshift.org/ocp/4.14:cli",
},
},
},
{
name: "from matches only its base image with COPY",
dockerfile: `FROM registry.ci.openshift.org/ocp/4.18:base AS builder
FROM registry.ci.openshift.org/openshift/release:rhel-9-release-golang-1.24-openshift-4.21
COPY --from=registry.ci.openshift.org/ocp/4.19:base /something /somewhere
RUN echo "hello"
`,
from: "src",
baseImages: map[string]api.ImageStreamTagReference{
"src": {Namespace: "ocp", Name: "4.19", Tag: "base"},
},
expected: map[string]api.ImageStreamTagReference{
"ocp_4.18_base": {
Namespace: "ocp",
Name: "4.18",
Tag: "base",
As: "registry.ci.openshift.org/ocp/4.18:base",
},
"ocp_4.19_base": {
Namespace: "ocp",
Name: "4.19",
Tag: "base",
As: "registry.ci.openshift.org/ocp/4.19:base",
"openshift_release_rhel-9-release-golang-1.24-openshift-4.21": {
Namespace: "openshift",
Name: "release",
Tag: "rhel-9-release-golang-1.24-openshift-4.21",
As: "registry.ci.openshift.org/openshift/release:rhel-9-release-golang-1.24-openshift-4.21",
},
},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
result := DetectInputsFromDockerfile([]byte(tc.dockerfile), tc.existingInputs, tc.from)
result := DetectInputsFromDockerfile([]byte(tc.dockerfile), tc.existingInputs, tc.from, tc.baseImages)

if diff := cmp.Diff(tc.expected, result); diff != "" {
t.Errorf("result differs from expected:\n%s", diff)
Expand Down