Skip to content

Commit 2b4263c

Browse files
Merge pull request #699 from openshift-cherrypick-robot/cherry-pick-696-to-v1.x
[v1.x] MULTIARCH-5767: Fix image processing when port is present
2 parents b229a99 + 90bcbbd commit 2b4263c

File tree

4 files changed

+499
-32
lines changed

4 files changed

+499
-32
lines changed

pkg/e2e/const.go

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,24 @@ const (
1414
)
1515

1616
const (
17-
MyFakeITMSAllowContactSourceTestMirrorRegistry = "my-fake-itms-allow-contact-source-mirror-registry.io"
18-
MyFakeITMSNeverContactSourceTestMirrorRegistry = "my-fake-itms-never-contact-source-mirror-registry.io"
19-
MyFakeIDMSNeverContactSourceTestSourceRegistry = "my-fake-idms-never-contact-source-source-registry.io"
20-
MyFakeICSPAllowContactSourceTestSourceRegistry = "my-fake-icsp-allow-contact-source-source-registry.io"
21-
HelloopenshiftPublicMultiarchImage = "quay.io/openshifttest/hello-openshift:1.2.0"
22-
HelloopenshiftPublicMultiarchImageDigest = "quay.io/openshifttest/hello-openshift@sha256:4200f438cf2e9446f6bcff9d67ceea1f69ed07a2f83363b7fb52529f7ddd8a83"
23-
HelloopenshiftPublicMultiarchImageTagDigest = "quay.io/openshifttest/hello-openshift:1.2.0@sha256:4200f438cf2e9446f6bcff9d67ceea1f69ed07a2f83363b7fb52529f7ddd8a83"
24-
SleepPublicMultiarchImage = "quay.io/openshifttest/sleep:1.2.0"
25-
RedisPublicMultiarchImage = "gcr.io/google_containers/redis:v1"
26-
PausePublicMultiarchImage = "gcr.io/google_containers/pause:3.2"
27-
ITMSName = "mto-itms-test"
28-
IDMSName = "mto-idms-test"
29-
ICSPName = "mto-icsp-test"
30-
RegistryNamespace = "registry"
31-
InsecureRegistryName = "insecure"
32-
NotTrustedRegistryName = "not-trusted"
33-
TrustedRegistryName = "trusted"
17+
MyFakeITMSAllowContactSourceTestMirrorRegistry = "my-fake-itms-allow-contact-source-mirror-registry.io"
18+
MyFakeITMSNeverContactSourceTestMirrorRegistry = "my-fake-itms-never-contact-source-mirror-registry.io"
19+
MyFakeIDMSNeverContactSourceTestSourceRegistry = "my-fake-idms-never-contact-source-source-registry.io"
20+
MyFakeICSPAllowContactSourceTestSourceRegistry = "my-fake-icsp-allow-contact-source-source-registry.io"
21+
HelloopenshiftPublicMultiarchImage = "quay.io/openshifttest/hello-openshift:1.2.0"
22+
HelloopenshiftPublicMultiarchImageDigest = "quay.io/openshifttest/hello-openshift@sha256:4200f438cf2e9446f6bcff9d67ceea1f69ed07a2f83363b7fb52529f7ddd8a83"
23+
HelloopenshiftPublicMultiarchImageTagDigest = "quay.io/openshifttest/hello-openshift:1.2.0@sha256:4200f438cf2e9446f6bcff9d67ceea1f69ed07a2f83363b7fb52529f7ddd8a83"
24+
HelloopenshiftPublicMultiarchImageWithPortDigest = "quay.io:443/openshifttest/hello-openshift@sha256:4200f438cf2e9446f6bcff9d67ceea1f69ed07a2f83363b7fb52529f7ddd8a83"
25+
HelloopenshiftPublicMultiarchImageWithPortTag = "quay.io:443/openshifttest/hello-openshift:1.2.0"
26+
HelloopenshiftPublicMultiarchImageWithPortTagDigest = "quay.io:443/openshifttest/hello-openshift:1.2.0@sha256:4200f438cf2e9446f6bcff9d67ceea1f69ed07a2f83363b7fb52529f7ddd8a83"
27+
SleepPublicMultiarchImage = "quay.io/openshifttest/sleep:1.2.0"
28+
RedisPublicMultiarchImage = "gcr.io/google_containers/redis:v1"
29+
PausePublicMultiarchImage = "gcr.io/google_containers/pause:3.2"
30+
ITMSName = "mto-itms-test"
31+
IDMSName = "mto-idms-test"
32+
ICSPName = "mto-icsp-test"
33+
RegistryNamespace = "registry"
34+
InsecureRegistryName = "insecure"
35+
NotTrustedRegistryName = "not-trusted"
36+
TrustedRegistryName = "trusted"
3437
)

pkg/e2e/podplacement/pod_placement_test.go

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1813,5 +1813,152 @@ var _ = Describe("The Pod Placement Operand", func() {
18131813
By("The pod should be running")
18141814
Eventually(framework.VerifyPodsAreRunning(ctx, client, ns, "app", "test"), e2e.WaitShort).Should(Succeed())
18151815
})
1816+
It("Should set node affinity when image has digest and port", func() {
1817+
var err error
1818+
By("Create an ephemeral namespace")
1819+
ns := framework.NewEphemeralNamespace()
1820+
err = client.Create(ctx, ns)
1821+
Expect(err).NotTo(HaveOccurred())
1822+
//nolint:errcheck
1823+
defer client.Delete(ctx, ns)
1824+
By("Create a deployment running image in source registry")
1825+
ps := NewPodSpec().
1826+
WithContainersImages(e2e.HelloopenshiftPublicMultiarchImageWithPortDigest).
1827+
Build()
1828+
d := NewDeployment().
1829+
WithSelectorAndPodLabels(podLabel).
1830+
WithPodSpec(ps).
1831+
WithReplicas(utils.NewPtr(int32(1))).
1832+
WithName("test-deployment").
1833+
WithNamespace(ns.Name).
1834+
Build()
1835+
err = client.Create(ctx, d)
1836+
Expect(err).NotTo(HaveOccurred())
1837+
archLabelNSR := NewNodeSelectorRequirement().
1838+
WithKeyAndValues(utils.ArchLabel, corev1.NodeSelectorOpIn, utils.ArchitectureAmd64,
1839+
utils.ArchitectureArm64, utils.ArchitectureS390x, utils.ArchitecturePpc64le).
1840+
Build()
1841+
expectedNSTs := NewNodeSelectorTerm().WithMatchExpressions(archLabelNSR).Build()
1842+
By("The pod should have been processed by the webhook and the scheduling gate label should be added")
1843+
Eventually(framework.VerifyPodLabels(ctx, client, ns, "app", "test", e2e.Present, schedulingGateLabel), e2e.WaitShort).Should(Succeed())
1844+
By("Verify arch label are set")
1845+
Eventually(framework.VerifyPodLabelsAreSet(ctx, client, ns, "app", "test",
1846+
utils.MultiArchLabel, "",
1847+
utils.ArchLabelValue(utils.ArchitectureAmd64), "",
1848+
utils.ArchLabelValue(utils.ArchitectureArm64), "",
1849+
utils.ArchLabelValue(utils.ArchitectureS390x), "",
1850+
utils.ArchLabelValue(utils.ArchitecturePpc64le), "",
1851+
), e2e.WaitShort).Should(Succeed())
1852+
By("Verify node affinity label are set correct")
1853+
Eventually(framework.VerifyPodLabelsAreSet(ctx, client, ns, "app", "test",
1854+
utils.NodeAffinityLabel, utils.NodeAffinityLabelValueSet,
1855+
utils.PreferredNodeAffinityLabel, utils.NodeAffinityLabelValueSet,
1856+
), e2e.WaitShort).Should(Succeed())
1857+
By("The pod should get node affinity of arch info because the mirror registries are functional.")
1858+
Eventually(framework.VerifyPodNodeAffinity(ctx, client, ns, "app", "test", *expectedNSTs), e2e.WaitShort).Should(Succeed())
1859+
By("The pod should have the preferred affinities set in the ClusterPodPlacementConfig")
1860+
Eventually(framework.VerifyPodPreferredNodeAffinity(ctx, client, ns, "app", "test",
1861+
defaultExpectedAffinityTerms()), e2e.WaitShort).Should(Succeed())
1862+
By("The pod should be running")
1863+
Eventually(framework.VerifyPodsAreRunning(ctx, client, ns, "app", "test"), e2e.WaitShort).Should(Succeed())
1864+
})
1865+
It("Should set node affinity when image has tag and port", func() {
1866+
var err error
1867+
By("Create an ephemeral namespace")
1868+
ns := framework.NewEphemeralNamespace()
1869+
err = client.Create(ctx, ns)
1870+
Expect(err).NotTo(HaveOccurred())
1871+
//nolint:errcheck
1872+
defer client.Delete(ctx, ns)
1873+
By("Create a deployment running image in source registry")
1874+
ps := NewPodSpec().
1875+
WithContainersImages(e2e.HelloopenshiftPublicMultiarchImageWithPortTag).
1876+
Build()
1877+
d := NewDeployment().
1878+
WithSelectorAndPodLabels(podLabel).
1879+
WithPodSpec(ps).
1880+
WithReplicas(utils.NewPtr(int32(1))).
1881+
WithName("test-deployment").
1882+
WithNamespace(ns.Name).
1883+
Build()
1884+
err = client.Create(ctx, d)
1885+
Expect(err).NotTo(HaveOccurred())
1886+
archLabelNSR := NewNodeSelectorRequirement().
1887+
WithKeyAndValues(utils.ArchLabel, corev1.NodeSelectorOpIn, utils.ArchitectureAmd64,
1888+
utils.ArchitectureArm64, utils.ArchitectureS390x, utils.ArchitecturePpc64le).
1889+
Build()
1890+
expectedNSTs := NewNodeSelectorTerm().WithMatchExpressions(archLabelNSR).Build()
1891+
By("The pod should have been processed by the webhook and the scheduling gate label should be added")
1892+
Eventually(framework.VerifyPodLabels(ctx, client, ns, "app", "test", e2e.Present, schedulingGateLabel), e2e.WaitShort).Should(Succeed())
1893+
By("Verify arch label are set")
1894+
Eventually(framework.VerifyPodLabelsAreSet(ctx, client, ns, "app", "test",
1895+
utils.MultiArchLabel, "",
1896+
utils.ArchLabelValue(utils.ArchitectureAmd64), "",
1897+
utils.ArchLabelValue(utils.ArchitectureArm64), "",
1898+
utils.ArchLabelValue(utils.ArchitectureS390x), "",
1899+
utils.ArchLabelValue(utils.ArchitecturePpc64le), "",
1900+
), e2e.WaitShort).Should(Succeed())
1901+
By("Verify node affinity label are set correct")
1902+
Eventually(framework.VerifyPodLabelsAreSet(ctx, client, ns, "app", "test",
1903+
utils.NodeAffinityLabel, utils.NodeAffinityLabelValueSet,
1904+
utils.PreferredNodeAffinityLabel, utils.NodeAffinityLabelValueSet,
1905+
), e2e.WaitShort).Should(Succeed())
1906+
By("The pod should get node affinity of arch info because the mirror registries are functional.")
1907+
Eventually(framework.VerifyPodNodeAffinity(ctx, client, ns, "app", "test", *expectedNSTs), e2e.WaitShort).Should(Succeed())
1908+
By("The pod should have the preferred affinities set in the ClusterPodPlacementConfig")
1909+
Eventually(framework.VerifyPodPreferredNodeAffinity(ctx, client, ns, "app", "test",
1910+
defaultExpectedAffinityTerms()), e2e.WaitShort).Should(Succeed())
1911+
By("The pod should be running")
1912+
Eventually(framework.VerifyPodsAreRunning(ctx, client, ns, "app", "test"), e2e.WaitShort).Should(Succeed())
1913+
})
1914+
It("Should set node affinity when image has digest and tag and port", func() {
1915+
var err error
1916+
By("Create an ephemeral namespace")
1917+
ns := framework.NewEphemeralNamespace()
1918+
err = client.Create(ctx, ns)
1919+
Expect(err).NotTo(HaveOccurred())
1920+
//nolint:errcheck
1921+
defer client.Delete(ctx, ns)
1922+
By("Create a deployment running image in source registry")
1923+
ps := NewPodSpec().
1924+
WithContainersImages(e2e.HelloopenshiftPublicMultiarchImageWithPortTagDigest).
1925+
Build()
1926+
d := NewDeployment().
1927+
WithSelectorAndPodLabels(podLabel).
1928+
WithPodSpec(ps).
1929+
WithReplicas(utils.NewPtr(int32(1))).
1930+
WithName("test-deployment").
1931+
WithNamespace(ns.Name).
1932+
Build()
1933+
err = client.Create(ctx, d)
1934+
Expect(err).NotTo(HaveOccurred())
1935+
archLabelNSR := NewNodeSelectorRequirement().
1936+
WithKeyAndValues(utils.ArchLabel, corev1.NodeSelectorOpIn, utils.ArchitectureAmd64,
1937+
utils.ArchitectureArm64, utils.ArchitectureS390x, utils.ArchitecturePpc64le).
1938+
Build()
1939+
expectedNSTs := NewNodeSelectorTerm().WithMatchExpressions(archLabelNSR).Build()
1940+
By("The pod should have been processed by the webhook and the scheduling gate label should be added")
1941+
Eventually(framework.VerifyPodLabels(ctx, client, ns, "app", "test", e2e.Present, schedulingGateLabel), e2e.WaitShort).Should(Succeed())
1942+
By("Verify arch label are set")
1943+
Eventually(framework.VerifyPodLabelsAreSet(ctx, client, ns, "app", "test",
1944+
utils.MultiArchLabel, "",
1945+
utils.ArchLabelValue(utils.ArchitectureAmd64), "",
1946+
utils.ArchLabelValue(utils.ArchitectureArm64), "",
1947+
utils.ArchLabelValue(utils.ArchitectureS390x), "",
1948+
utils.ArchLabelValue(utils.ArchitecturePpc64le), "",
1949+
), e2e.WaitShort).Should(Succeed())
1950+
By("Verify node affinity label are set correct")
1951+
Eventually(framework.VerifyPodLabelsAreSet(ctx, client, ns, "app", "test",
1952+
utils.NodeAffinityLabel, utils.NodeAffinityLabelValueSet,
1953+
utils.PreferredNodeAffinityLabel, utils.NodeAffinityLabelValueSet,
1954+
), e2e.WaitShort).Should(Succeed())
1955+
By("The pod should get node affinity of arch info because the mirror registries are functional.")
1956+
Eventually(framework.VerifyPodNodeAffinity(ctx, client, ns, "app", "test", *expectedNSTs), e2e.WaitShort).Should(Succeed())
1957+
By("The pod should have the preferred affinities set in the ClusterPodPlacementConfig")
1958+
Eventually(framework.VerifyPodPreferredNodeAffinity(ctx, client, ns, "app", "test",
1959+
defaultExpectedAffinityTerms()), e2e.WaitShort).Should(Succeed())
1960+
By("The pod should be running")
1961+
Eventually(framework.VerifyPodsAreRunning(ctx, client, ns, "app", "test"), e2e.WaitShort).Should(Succeed())
1962+
})
18161963
})
18171964
})

pkg/image/inspector.go

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -208,30 +208,52 @@ func (i *registryInspector) GetCompatibleArchitecturesSet(ctx context.Context, i
208208
// 2. A digest-only reference if a digest is present, dropping the tag when both
209209
// a tag and digest are specified in the pod's container image field
210210
func parseImageReference(imageName string) (string, error) {
211-
digestSplit := strings.Split(imageName, "@sha256:")
212-
switch len(digestSplit) {
213-
case 0:
211+
// Check for empty image name first
212+
if imageName == "" {
214213
return "", errors.New("invalid image name, must not be empty")
215-
case 1:
214+
}
215+
216+
// Find digest separator (supports sha256, sha512, sha384, etc.)
217+
digestIdx := strings.Index(imageName, "@sha")
218+
219+
// Check if digest is present
220+
switch digestIdx {
221+
case -1:
222+
// No digest is present
216223
return imageName, nil
217-
case 2:
224+
case 0:
225+
// Image name can't start with digest
226+
return "", errors.New("invalid image name, image must not be empty")
218227
default:
228+
// Now check that if there is a tag present
229+
}
230+
231+
// Validate there's only one digest
232+
if strings.Count(imageName[digestIdx+1:], "@sha") > 0 {
219233
return "", errors.New("invalid image name, must only have one digest")
220234
}
221235

222-
// Since the length is 2, imageName is either a digest-only image or both the tag and the digest have been provided.
223-
tagSplit := strings.Split(digestSplit[0], ":")
224-
switch len(tagSplit) {
225-
case 0:
226-
return "", errors.New("invalid image name, image must not be empty")
227-
case 1:
228-
// Since the length is 1, no tag has been found: imageName is a digest-only image.
236+
namePart := imageName[:digestIdx]
237+
digestPart := imageName[digestIdx:] // includes the @
238+
239+
// Find last "/" and last ":" to determine if we have a tag to remove
240+
// Format: [registry[:port]/][namespace/]image[:tag]@digest
241+
lastSlash := strings.LastIndex(namePart, "/")
242+
lastColon := strings.LastIndex(namePart, ":")
243+
244+
//determine what to do with colons
245+
switch {
246+
case lastColon == -1:
247+
// No colon at all - no port, no tag
229248
return imageName, nil
230-
case 2:
249+
case lastColon > lastSlash:
250+
// Colon is after last slash (or no slash) - it's a tag, remove it
251+
namePart = namePart[:lastColon]
252+
return namePart + digestPart, nil
231253
default:
232-
return "", errors.New("invalid image name, image contains more than one digest or tag")
254+
// Colon is before last slash - it's a port, keep it
255+
return imageName, nil
233256
}
234-
return tagSplit[0] + "@sha256:" + digestSplit[1], nil
235257
}
236258

237259
func isBundleImage(image ociv1.ImageConfig) bool {

0 commit comments

Comments
 (0)