Skip to content

Conversation

@QiWang19
Copy link
Member

@QiWang19 QiWang19 commented Apr 5, 2022

Migrate ICSP to ImageDigest,TagMirrorSet.
MCO has handled the object conversoin from icsp to ImageDiegstMirrorSet objects.

Epic: https://issues.redhat.com/browse/OCPNODE-521
Epic: https://issues.redhat.com/browse/OCPNODE-810

Signed-off-by: Qi Wang [email protected]

Copy link
Contributor

@mtrmac mtrmac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just an extremely brief skim for now, not really a review.

Let’s start with the ICSP discussion.

// mergedDigestMirrorSets processes idmsRules and returns a set of ImageDigestMirrors, one for each Source value,
// ordered consistently with the preference order of the individual entries (if possible)
// E.g. given mirror sets (B, C) and (A, B), it will combine them into a single (A, B, C) set.
func mergedDigestMirrorSets(idmsRules []*apicfgv1.ImageDigestMirrorSet) ([]apicfgv1.ImageDigestMirrors, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At a first glance it seems to me that it should be possible to share more of the logic, but I haven’t read the code that carefully. (I do appreciate there’s a topoSortRepos now.)

Maybe an object that maintains disjointSets/mirrorBlockSource, with methods very vaguely like addMirrorSet(source, neverContactSource, mirrors) and mergedMirrorSets() []struct{source, neverContactSource, mirrors}? That way the two functions here would deal only with the data conversion with no logic at all.

Or maybe, instead of mergedMirrorSets, a sourceList() []string and updateRegistry(source, *Registry), to share more of the code in EditRegistriesConfig that just consumes the data from mergedMirrorSets.

@QiWang19
Copy link
Member Author

QiWang19 commented Apr 6, 2022

@mtrmac PTAL. More code got shared.

@nee1esh
Copy link

nee1esh commented Apr 6, 2022

/retest

openshift-merge-robot pushed a commit to openshift/release that referenced this pull request Apr 6, 2022
Update go version from 1.16 to 1.17.
openshift/runtime-utils#15  CI failed since the go is out of date,
update the go version to fix it.

Signed-off-by: Qi Wang <[email protected]>
@QiWang19
Copy link
Member Author

QiWang19 commented Apr 6, 2022

/retest

@QiWang19 QiWang19 changed the title [OCPNODE-553] Add support ImageDigest,TagMirrorSet CRDs [OCPNODE-553] [WIP] Add support ImageDigest,TagMirrorSet CRDs Apr 8, 2022
@openshift-bot
Copy link

Issues go stale after 90d of inactivity.

Mark the issue as fresh by commenting /remove-lifecycle stale.
Stale issues rot after an additional 30d of inactivity and eventually close.
Exclude this issue from closing by commenting /lifecycle frozen.

If this issue is safe to close now please do so with /close.

/lifecycle stale

@openshift-ci openshift-ci bot added lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale. needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. labels Jul 8, 2022
@openshift-ci
Copy link

openshift-ci bot commented Jul 8, 2022

@QiWang19: PR needs rebase.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@openshift-bot
Copy link

Stale issues rot after 30d of inactivity.

Mark the issue as fresh by commenting /remove-lifecycle rotten.
Rotten issues close after an additional 30d of inactivity.
Exclude this issue from closing by commenting /lifecycle frozen.

If this issue is safe to close now please do so with /close.

/lifecycle rotten
/remove-lifecycle stale

@openshift-ci openshift-ci bot added lifecycle/rotten Denotes an issue or PR that has aged beyond stale and will be auto-closed. and removed lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale. labels Aug 7, 2022
@openshift-merge-robot openshift-merge-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 16, 2022
@QiWang19 QiWang19 changed the title [OCPNODE-553] [WIP] Add support ImageDigest,TagMirrorSet CRDs [OCPNODE-553] Migrate ICSP to ImageDigest,TagMirrorSet CRDs Aug 17, 2022
@QiWang19
Copy link
Member Author

@mtrmac could you review it? ICSP object migration should be done in MCO, this PR update uses new APIs.

@rphillips
Copy link
Contributor

/remove-lifecycle rotten

@openshift-ci openshift-ci bot removed the lifecycle/rotten Denotes an issue or PR that has aged beyond stale and will be auto-closed. label Aug 17, 2022
@rphillips
Copy link
Contributor

rphillips commented Aug 17, 2022

The pointer indirection is likely not needed. Can we refactor this by removing the pointer indirection such that the functions do not take a pointer, like:

disjointSets: map[string]*[]*[]apicfgv1.ImageMirror{},

and

func (sets *mirrorSets) addMirrorSets(source string, mirrorSourcePolicy apicfgv1.MirrorSourcePolicy, mirrors *[]apicfgv1.ImageMirror)

The mirrors attribute probably doesn't need to be a pointer. Likewise for the other functions in the file.

@mtrmac
Copy link
Contributor

mtrmac commented Aug 18, 2022

@mtrmac could you review it? ICSP object migration should be done in MCO, this PR update uses new APIs.

Is that viable?

(It might well be fine. It’s just not what I vaguely remember the earlier migration conversations to be like, and I just thought I’d ask before we commit to that direction.)

@mtrmac
Copy link
Contributor

mtrmac commented Aug 19, 2022

So is the idea that MCO is going to immediately convert, and delete, every created ICSP , and API consumers can then only deal with the new types?

I’m sorry, actually reading the code, MCO is keeping the ICSP resources around, so they are still available to old consumers.

That seems perfect to me. Back to actually reading this PR…

@QiWang19 QiWang19 force-pushed the mirrorset-crd branch 2 times, most recently from 2e6d143 to bb15d8e Compare August 31, 2022 20:26
QiWang19 added a commit to QiWang19/release that referenced this pull request Sep 1, 2022
Upgrade to go 1.18, openshift/runtime-utils#15
failed the ci since go is out of date.

Signed-off-by: Qi Wang <[email protected]>
Copy link
Contributor

@mtrmac mtrmac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two API semantics questions

And I’d like to push a bit further on the code sharing: The need to propagate blocked/insecure to sub-scopes is somewhat non-obvious, and having that triplicated feels a bit risky to me.

for _, mirror := range set.Mirrors {
if mirror != set.Source {
// rdmContainsARealMirror returns true if mirrors contains at least one entry that is not source.
func rdmContainsARealMirror(source string, mirrors []apicfgv1.ImageMirror) bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rdm refers to RepositoryDigestMirrors, so this should probably be renamed. mirrorsContainARealMirror? mirrorsContainSomeOtherThan?

}
}

// sourceList collects the mirror sources and sort them in increasing order
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// sourceList collects the mirror sources and sort them in increasing order
// sourceList collects the mirror sources and returns them sorted in increasing order

to be a bit clearer that this this does not affect the mirrorSets object.


func (sets *mirrorSets) addMirrorSets(source string, mirrorSourcePolicy apicfgv1.MirrorSourcePolicy, mirrors []apicfgv1.ImageMirror) {
if !rdmContainsARealMirror(source, mirrors) {
return // No mirrors (or mirrors that only repeat the authoritative source) is not really a mirror set.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to have such a no-mirror object with NeverContactSource? If so, should we still set mirrorBlockSource?

(I’m most tempted to say that such an object is invalid and should be rejected, I’m not sure if it is practical. If it isn’t, both setting mirrorBlockSource and the current approach is, I guess, acceptable, but with the current approach, a extending the comment that yes, we ignore mirrorSourcePolicy and that’s intentional, would be useful.)

Comment on lines 113 to 148
sources := tagMirrorSets.sourceList()
// Convert the sets of mirrors
res := []apicfgv1.ImageTagMirrors{}
for _, source := range sources {
source, mirrors, err := tagMirrorSets.mergedMirrors(source)
if err != nil {
return nil, err
}
imageTagMirror := apicfgv1.ImageTagMirrors{
Source: source,
Mirrors: mirrors,
}
if tagMirrorSets.mirrorBlockSource[source] {
imageTagMirror.MirrorSourcePolicy = apicfgv1.NeverContactSource
}
res = append(res, imageTagMirror)
}
sort.Strings(sources)
return res, nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t think we need to return correctly-typed Image{Tag,Digest}Mirrors from the pair of these functions — so we could define a local type mergedMirrorsItem struct { source, mirrors, mirrorSourcePolicy } and all of this loop could be also shared.

return res, nil
}

func topoSortRepos(source string, ds *[][]apicfgv1.ImageMirror) ([]string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not just topological sort. Maybe {shared,raw}MergedMirrors? (Or, alternatively, if the loop calling this can be made shared, it can be inlined back. OTOH having this code as a separate function might be a better granularity for unit testing.)

set := apioperatorsv1alpha1.RepositoryDigestMirrors{
Source: source,
Mirrors: tt.mirrors,
set := &apicfgv1.ImageTagMirrorSet{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking: If set is not used other than to read one of its fields, it doesn’t need to be constructed.

_, err := toml.Decode(string(templateBytes), &config)
require.NoError(t, err)
err = EditRegistriesConfig(&config, tt.insecure, tt.blocked, tt.icspRules, tt.idmsRules, tt.itmsRules)
require.NotNil(t, err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely non-blocking: assert.NoError would be a better match here.

idmsRules []*apicfgv1.ImageDigestMirrorSet
itmsRules []*apicfgv1.ImageTagMirrorSet
icspRules []*apioperatorsv1alpha1.ImageContentSourcePolicy
want sysregistriesv2.V2RegistriesConf
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

insecure, blocked, want are unused, so that can be dropped.

Non-blocking: This test could be shorter and somewhat easier to fit on the screen with:

  • Only including one set of mirrors instead of three in every item.
  • Making the test data idmsNonempty, itmsNonempty, icspNonempty bool, and letting the testing loop provide data or an empty array.

Comment on lines 764 to 582
Spec: apicfgv1.ImageTagMirrorSetSpec{
ImageTagMirrors: []apicfgv1.ImageTagMirrors{ // other.com is neither insecure nor blocked
{Source: "insecure.com/ns-i1", Mirrors: []apicfgv1.ImageMirror{"blocked.com/ns-b1", "other.com/ns-o1"}},
{Source: "blocked.com/ns-b/ns2-b", Mirrors: []apicfgv1.ImageMirror{"other.com/ns-o2", "insecure.com/ns-i2"}},
{Source: "other.com/ns-o3", Mirrors: []apicfgv1.ImageMirror{"insecure.com/ns-i2", "blocked.com/ns-b/ns3-b", "foo.insecure-example.com/bar"}},
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test does not set anything to insecure nor blocked, so using the insecure.com/blocked.com domains is a bit unexpected.

What is being tested here? Just a smoke-test of correctly creating tag entries? Two entries would be enough for that.

@mtrmac
Copy link
Contributor

mtrmac commented Sep 1, 2022

The failing tests do seem to suggest that updating to Go ≥ 1.18 is required to be able to consume the new API module.

@mtrmac
Copy link
Contributor

mtrmac commented Sep 1, 2022

The failing tests do seem to suggest that updating to Go ≥ 1.18 is required to be able to consume the new API module.

Ah, that’s being handled in openshift/release#31916 .

openshift-merge-robot pushed a commit to openshift/release that referenced this pull request Sep 2, 2022
Upgrade to go 1.18, openshift/runtime-utils#15
failed the ci since go is out of date.

Signed-off-by: Qi Wang <[email protected]>

Signed-off-by: Qi Wang <[email protected]>
@QiWang19
Copy link
Member Author

QiWang19 commented Sep 3, 2022

@mtrmac PTAL

Copy link
Contributor

@mtrmac mtrmac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

Implementation LGTM, just a few cleanup suggestions. Looking at the tests, I’m unsure why various tests were just dropped.


// Sort the sets of mirrors by Source to ensure deterministic output
// sourceList collects the mirror sources and returns them sorted in increasing order
func (sets *mirrorSets) sourceList() []string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This now has only one caller and it can be inlined back.

return sources
}

func (sets *mirrorSets) addMirrorSets(source string, mirrorSourcePolicy apicfgv1.MirrorSourcePolicy, mirrors []apicfgv1.ImageMirror) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
func (sets *mirrorSets) addMirrorSets(source string, mirrorSourcePolicy apicfgv1.MirrorSourcePolicy, mirrors []apicfgv1.ImageMirror) {
func (sets *mirrorSets) addMirrorSet(source string, mirrorSourcePolicy apicfgv1.MirrorSourcePolicy, mirrors []apicfgv1.ImageMirror) {

sets is multiple sets, this is adding a single one.

Comment on lines +76 to +69
strMirrors := []string{}
for _, m := range mirrors {
strMirrors = append(strMirrors, (string(m)))
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely non-blocking: This copy is probably avoidable, perhaps with some more casts in the merging implementation.

(It’s a bit weird for ICSP to do a copy to convert to ImageMirror, only for this code to convert back to strings.)

But the cost is trivial either way, I’m not worried about it at all.

}

// mergedMirrors generates deterministic order of mirrors for a given source
func (sets *mirrorSets) mergedMirrors(source string) (string, []string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn’t need to return source, it’s just the caller-supplied value.

return source, sortedRepos, nil
}

type mergedMirrorsItem struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading the full context, (contrary to my earlier suggestion — my mistake) mergedMirrorSet is probably the right name. That’s what it is (unlike “item”, which says nothing), and both functions like mergedICSPMirrorSets and variables like tagMirrorSets use that naming.

})
}
tcRes := tc.result
if tc.name == "Separate mirror sets" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do this in the data.

Comment on lines 205 to 208
if err != nil {
t.Errorf("Error %v", err)
return
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely non-blocking: require.NoError(t, err) is shorter. This works just fine — and I realize the original was this way as well.

idmsRules: []*apicfgv1.ImageDigestMirrorSet{
{
Spec: apicfgv1.ImageDigestMirrorSetSpec{
ImageDigestMirrors: []apicfgv1.ImageDigestMirrors{ // other.com is neither insecure nor blocked
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment seems inapplicable.

itmsRules: []*apicfgv1.ImageTagMirrorSet{
{
Spec: apicfgv1.ImageTagMirrorSetSpec{
ImageTagMirrors: []apicfgv1.ImageTagMirrors{ // other.com is neither insecure nor blocked
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment seems inapplicable.

{Source: "source.example.net", Mirrors: []string{"z1.example.net", "y2.example.net", "x3.example.net"}},
},
},
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The following test cases were dropped. Is there a specific reason for that? If not, please add them back.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added back. Accidently removed during the last code cleanup.

@QiWang19 QiWang19 force-pushed the mirrorset-crd branch 2 times, most recently from eca703f to c29fe4c Compare September 7, 2022 00:05
@QiWang19
Copy link
Member Author

QiWang19 commented Sep 7, 2022

@mtrmac PTAL.

Copy link
Contributor

@mtrmac mtrmac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One very last thing - the tc.name == … condition.

LGTM otherwise.

}
sourceInGraph := false
for _, m := range mirrors {
if string(m) == source {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking: The string() cast can now be dropped.

Comment on lines 157 to 158
for i := range itms.Spec.ImageTagMirrors {
set := itms.Spec.ImageTagMirrors[i]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely non-blocking:

for _, set := range … { /* use set */ }

would be one variable and one line shorter. (In all three instances.)

reg.Mirrors = updated
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Absolutely non-blocking: drop this line? I don’t think it separates “paragraphs” / individual ideas when placed here.)

func TestMergedICSPMirrorSets(t *testing.T) {
for _, tc := range mergedMirrorsetsTestcases {
t.Run(tc.name, func(t *testing.T) {
if tc.name == "Separate mirror sets with mirrorSourcePolicy set" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make this driven by a real data field.

Either here,

if tc.requiresMirrorSourcePolicy { t.Skip() }

(and set the tc.requiresMirrorSourcePolicy field on the test case), or (preferably?) make this fully automatic below, with something like

if item.mirrorSourcePolicy != "" {
    t.Skip("…")
}

Add support for ImageDigestMirrorSet, ImageTagMirrorSet CRD

Epic: https://issues.redhat.com/browse/OCPNODE-521
Epic: https://issues.redhat.com/browse/OCPNODE-810

Signed-off-by: Qi Wang <[email protected]>
Signed-off-by: Qi Wang <[email protected]>
@openshift-ci
Copy link

openshift-ci bot commented Sep 26, 2022

@QiWang19: all tests passed!

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here.

@mtrmac
Copy link
Contributor

mtrmac commented Sep 26, 2022

/approve

@openshift-ci openshift-ci bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Sep 26, 2022
@mtrmac
Copy link
Contributor

mtrmac commented Sep 26, 2022

/lgtm

@openshift-ci openshift-ci bot added the lgtm Indicates that a PR is ready to be merged. label Sep 26, 2022
@openshift-ci
Copy link

openshift-ci bot commented Sep 26, 2022

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: mtrmac, QiWang19

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-merge-robot openshift-merge-robot merged commit 5c488b2 into openshift:master Sep 26, 2022
@mtrmac
Copy link
Contributor

mtrmac commented Sep 26, 2022

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. lgtm Indicates that a PR is ready to be merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants