diff --git a/Gopkg.lock b/Gopkg.lock index 2e82e9394f..af4090452e 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -423,7 +423,7 @@ [[projects]] branch = "master" - digest = "1:e92977080ffd645c40ccbaff10d915ab89c4e6556e84e2d0900f4c40d331a744" + digest = "1:c94bba30993ebfe6eb036983b04ad6ac6b89377d2f6dfeb729f39109ddbda053" name = "github.com/openshift/api" packages = [ "config/v1", @@ -431,7 +431,7 @@ "operator/v1alpha1", ] pruneopts = "NUT" - revision = "25a4eebe16c8bcb53e28605f8d5b91df392daeba" + revision = "9525304a0adb725ab4a4a54539a1a6bf6cc343d3" [[projects]] branch = "master" diff --git a/pkg/controller/container-runtime-config/container_runtime_config_controller_test.go b/pkg/controller/container-runtime-config/container_runtime_config_controller_test.go index 59475769da..1391b4dc72 100644 --- a/pkg/controller/container-runtime-config/container_runtime_config_controller_test.go +++ b/pkg/controller/container-runtime-config/container_runtime_config_controller_test.go @@ -592,7 +592,7 @@ func TestICSPUpdate(t *testing.T) { mcs1 := helpers.NewMachineConfig(getManagedKeyReg(mcp), map[string]string{"node-role": "master"}, "dummy://", []igntypes.File{{}}) mcs2 := helpers.NewMachineConfig(getManagedKeyReg(mcp2), map[string]string{"node-role": "worker"}, "dummy://", []igntypes.File{{}}) icsp := newICSP("built-in", []apioperatorsv1alpha1.RepositoryDigestMirrors{ - {Sources: []string{"built-in1.example.com", "built-in-2.example.com"}}, + {Source: "built-in-source.example.com", Mirrors: []string{"built-in-mirror.example.com"}}, }) f.ccLister = append(f.ccLister, cc) @@ -630,7 +630,7 @@ func TestICSPUpdate(t *testing.T) { // Modify ICSP icspUpdate := icsp.DeepCopy() icspUpdate.Spec.RepositoryDigestMirrors = append(icspUpdate.Spec.RepositoryDigestMirrors, apioperatorsv1alpha1.RepositoryDigestMirrors{ - Sources: []string{"local-mirror.local", "built-in1.example.com"}, + Source: "built-in-source.example.com", Mirrors: []string{"local-mirror.local"}, }) f.ccLister = append(f.ccLister, cc) diff --git a/pkg/controller/container-runtime-config/helpers.go b/pkg/controller/container-runtime-config/helpers.go index f0425ef7ba..c0d479bded 100644 --- a/pkg/controller/container-runtime-config/helpers.go +++ b/pkg/controller/container-runtime-config/helpers.go @@ -253,87 +253,67 @@ func scopeMatchesRegistry(scope, reg string) bool { return false } -// disjointOrderedMirrorSets processes icspRules and returns the resulting disjoint sets of mutual mirrors, each ordered in consistently with -// the icspRules preference order (if possible) -// This exists to merge different mirror sets, e.g. given mirror sets (B, C) and (A, B), it will combine them into a single (A, B, C) set. -func disjointOrderedMirrorSets(icspRules []*apioperatorsv1alpha1.ImageContentSourcePolicy) ([][]string, error) { - // Collect all mirror sets into a flat array to abstract the rest of the code from the ImageContentSourcePolicy format - mirrorSets := []*apioperatorsv1alpha1.RepositoryDigestMirrors{} +// mergedMirrorSets processes icspRules and returns a set of RepositoryDigestMirrors, 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 mergedMirrorSets(icspRules []*apioperatorsv1alpha1.ImageContentSourcePolicy) ([]apioperatorsv1alpha1.RepositoryDigestMirrors, error) { + disjointSets := map[string]*[]*apioperatorsv1alpha1.RepositoryDigestMirrors{} // Key == Source for _, icsp := range icspRules { for i := range icsp.Spec.RepositoryDigestMirrors { set := &icsp.Spec.RepositoryDigestMirrors[i] - if len(set.Sources) < 2 { - // No mirrors, or a single repository with no mirror relationship, is not really a mirror set. - continue + if len(set.Mirrors) == 0 { + continue // No mirrors is not really a mirror set. } - mirrorSets = append(mirrorSets, set) - } - } - - // Build a graph with nodes == mirrors, union each mirror set. - unionGraph := newUnionGraph() - for _, set := range mirrorSets { - // Given set.Sources = (A,B,C,D), Union (A,B), (A,C), (A,D): - m1 := set.Sources[0] // The build of mirrorSets guarantees len(set.Sources) >= 2 - for _, m2 := range set.Sources[1:] { - unionGraph.Union(m1, m2) - } - } - // Enumerate disjoint components - disjointSets := map[unionNode]*[]*apioperatorsv1alpha1.RepositoryDigestMirrors{} // Key == the root returned by unionGraph.Find() - for _, set := range mirrorSets { - // The build of mirrorSets guarantees len(set.Sources) >= 2 - root := unionGraph.Find(set.Sources[0]) // == union.Find(set.Sources[x]) for any other x - ds, ok := disjointSets[root] - if !ok { - ds = &[]*apioperatorsv1alpha1.RepositoryDigestMirrors{} - disjointSets[root] = ds + ds, ok := disjointSets[set.Source] + if !ok { + ds = &[]*apioperatorsv1alpha1.RepositoryDigestMirrors{} + disjointSets[set.Source] = ds + } + *ds = append(*ds, set) } - *ds = append(*ds, set) } - // Sort the sets of mirrors to ensure deterministic output - type dsOrderedKey struct { - mapKey unionNode - sortKey string // == The legicographically first mirror in the disjointSets element - } - dsKeys := []dsOrderedKey{} - for mapKey, ds := range disjointSets { - sortKey := "" - // The build of disjointSets guarantees len(ds) > 0, len(ds[0].Sources) >= 2 - for _, set := range *ds { - for _, src := range set.Sources { - if sortKey == "" || sortKey > src { - sortKey = src - } - } - } - dsKeys = append(dsKeys, dsOrderedKey{ - mapKey: mapKey, - sortKey: sortKey, - }) + // Sort the sets of mirrors by Source to ensure deterministic output + sources := []string{} + for key := range disjointSets { + sources = append(sources, key) } - sort.Slice(dsKeys, func(i, j int) bool { - return dsKeys[i].sortKey < dsKeys[j].sortKey - }) + sort.Strings(sources) // Convert the sets of mirrors - res := [][]string{} - for _, dsKey := range dsKeys { - ds := disjointSets[dsKey.mapKey] + res := []apioperatorsv1alpha1.RepositoryDigestMirrors{} + for _, source := range sources { + ds := disjointSets[source] topoGraph := newTopoGraph() for _, set := range *ds { - // The build of mirrorSets guarantees len(set.Sources) >= 2, i.e. that this loop runs at least once for every set, - // and that every node in topoGraph is implicitly added by topoGraph.AddEdge (there are no unconnected nodes that we would - // have to add separately from the edges). - for i := 0; i+1 < len(set.Sources); i++ { - topoGraph.AddEdge(set.Sources[i], set.Sources[i+1]) + for i := 0; i+1 < len(set.Mirrors); i++ { + topoGraph.AddEdge(set.Mirrors[i], set.Mirrors[i+1]) + } + sourceInGraph := false + for _, m := range set.Mirrors { + if m == source { + sourceInGraph = true + break + } } + if !sourceInGraph { + // The build of mirrorSets guarantees len(set.Mirrors) > 0. + topoGraph.AddEdge(set.Mirrors[len(set.Mirrors)-1], source) + } + // Every node in topoGraph, including source, is implicitly added by topoGraph.AddEdge (there are no unconnected nodes that we would + // have to add separately from the edges). } sortedRepos, err := topoGraph.Sorted() if err != nil { return nil, err } - res = append(res, sortedRepos) + if sortedRepos[len(sortedRepos)-1] == source { + // We don't need to explicitly include source in the list, it will be automatically tried last per the semantics of sysregistriesv2. Mirrors. + sortedRepos = sortedRepos[:len(sortedRepos)-1] + } + res = append(res, apioperatorsv1alpha1.RepositoryDigestMirrors{ + Source: source, + Mirrors: sortedRepos, + }) } return res, nil } @@ -361,17 +341,15 @@ func updateRegistriesConfig(data []byte, internalInsecure, internalBlocked []str return &tomlConf.Registries[len(tomlConf.Registries)-1] } - mirrorSets, err := disjointOrderedMirrorSets(icspRules) + mirrorSets, err := mergedMirrorSets(icspRules) if err != nil { return nil, err } - for _, sortedRepos := range mirrorSets { - for _, primaryRepo := range sortedRepos { - reg := getRegistryEntry(primaryRepo) - reg.MirrorByDigestOnly = true - for _, mirror := range sortedRepos { - reg.Mirrors = append(reg.Mirrors, sysregistriesv2.Endpoint{Location: mirror}) - } + for _, mirrorSet := range mirrorSets { + reg := getRegistryEntry(mirrorSet.Source) + reg.MirrorByDigestOnly = true + for _, mirror := range mirrorSet.Mirrors { + reg.Mirrors = append(reg.Mirrors, sysregistriesv2.Endpoint{Location: mirror}) } } diff --git a/pkg/controller/container-runtime-config/helpers_test.go b/pkg/controller/container-runtime-config/helpers_test.go index aa4f6b3800..0388671cd6 100644 --- a/pkg/controller/container-runtime-config/helpers_test.go +++ b/pkg/controller/container-runtime-config/helpers_test.go @@ -39,98 +39,113 @@ func TestScopeMatchesRegistry(t *testing.T) { } } -func TestDisjointOrderedMirrorSets(t *testing.T) { +func TestMergedMirorSets(t *testing.T) { for _, c := range []struct { name string - input [][][]string - result [][]string + input [][]apioperatorsv1alpha1.RepositoryDigestMirrors + result []apioperatorsv1alpha1.RepositoryDigestMirrors }{ { name: "Empty", - input: [][][]string{}, - result: [][]string{}, + input: [][]apioperatorsv1alpha1.RepositoryDigestMirrors{}, + result: []apioperatorsv1alpha1.RepositoryDigestMirrors{}, }, { name: "Irrelevant singletons", - input: [][][]string{ + input: [][]apioperatorsv1alpha1.RepositoryDigestMirrors{ { - nil, - {}, - }, - { - {"a.example.com"}, - {"b.example.com"}, + {Source: "a.example.com", Mirrors: nil}, + {Source: "b.example.com", Mirrors: []string{}}, }, }, - result: [][]string{}, + result: []apioperatorsv1alpha1.RepositoryDigestMirrors{}, }, // The registry names below start with an irrelevant letter, usually counting from the end of the alphabet, to verify that // the result is based on the order in the Sources array and is not just alphabetically-sorted. { - name: "Separate ordered sets", - input: [][][]string{ + name: "Separate mirror sets", + input: [][]apioperatorsv1alpha1.RepositoryDigestMirrors{ { - {"z1.example.net", "y2.example.net", "x3.example.net"}, + {Source: "source.example.net", Mirrors: []string{"z1.example.net", "y2.example.net", "x3.example.net"}}, }, { - {"z1.example.com", "y2.example.com", "x3.example.com"}, + {Source: "source.example.com", Mirrors: []string{"z1.example.com", "y2.example.com", "x3.example.com"}}, }, }, - result: [][]string{ - {"z1.example.com", "y2.example.com", "x3.example.com"}, - {"z1.example.net", "y2.example.net", "x3.example.net"}, + result: []apioperatorsv1alpha1.RepositoryDigestMirrors{ + {Source: "source.example.com", Mirrors: []string{"z1.example.com", "y2.example.com", "x3.example.com"}}, + {Source: "source.example.net", Mirrors: []string{"z1.example.net", "y2.example.net", "x3.example.net"}}, }, }, { name: "Sets with a shared element - strict order", - input: [][][]string{ + input: [][]apioperatorsv1alpha1.RepositoryDigestMirrors{ { - {"z1.example.net", "y2.example.net"}, - {"z1.example.com", "y2.example.com"}, + {Source: "source.example.net", Mirrors: []string{"z1.example.net", "y2.example.net"}}, + {Source: "source.example.com", Mirrors: []string{"z1.example.com", "y2.example.com"}}, }, { - {"y2.example.net", "x3.example.net"}, - {"y2.example.com", "x3.example.com"}, + {Source: "source.example.net", Mirrors: []string{"y2.example.net", "x3.example.net"}}, + {Source: "source.example.com", Mirrors: []string{"y2.example.com", "x3.example.com"}}, + }, + }, + result: []apioperatorsv1alpha1.RepositoryDigestMirrors{ + {Source: "source.example.com", Mirrors: []string{"z1.example.com", "y2.example.com", "x3.example.com"}}, + {Source: "source.example.net", Mirrors: []string{"z1.example.net", "y2.example.net", "x3.example.net"}}, + }, + }, + { + // This is not technically impossible, and it could be in principle used to set up last-fallback mirrors that + // are only accessed if the source is not available. + // WARNING: The order in this case is unspecified by the ICSP specification, and may change at any time; + // this test case only ensures that the corner case is handled reasonably, and that the output is stable + // (i.e. the operator does not cause unnecessary changes in output objects.) + name: "Source included in mirrors", + input: [][]apioperatorsv1alpha1.RepositoryDigestMirrors{ + { + {Source: "source.example.com", Mirrors: []string{"z1.example.com", "source.example.com", "y2.example.com"}}, + {Source: "source.example.com", Mirrors: []string{"source.example.com", "y2.example.com", "x3.example.com"}}, }, }, - result: [][]string{ - {"z1.example.com", "y2.example.com", "x3.example.com"}, - {"z1.example.net", "y2.example.net", "x3.example.net"}, + result: []apioperatorsv1alpha1.RepositoryDigestMirrors{ + {Source: "source.example.com", Mirrors: []string{"z1.example.com", "source.example.com", "y2.example.com", "x3.example.com"}}, }, }, // More complex mirror set combinations are mostly tested in TestTopoGraph { name: "Example", - input: [][][]string{ + input: [][]apioperatorsv1alpha1.RepositoryDigestMirrors{ { // Vendor-provided default configuration - {"registry1.vendor.com", "registry2.vendor.com"}, + {Source: "source.vendor.com", Mirrors: []string{"registry2.vendor.com"}}, }, { // Vendor2-provided default configuration - {"registry1.vendor2.com", "registry2.vendor2.com"}, + {Source: "source.vendor2.com", Mirrors: []string{"registry1.vendor2.com", "registry2.vendor2.com"}}, }, { // Admin-configured local mirrors: - {"local-mirror.example.com", "registry1.vendor.com"}, - {"local-mirror2.example.com", "registry2.vendor2.com", "registry1.vendor2.com"}, // Opposite order of the vendor’s mirrors + {Source: "source.vendor.com", Mirrors: []string{"local-mirror.example.com"}}, + // Opposite order of the vendor’s mirrors. + // WARNING: The order in this case is unspecified by the ICSP specification, and may change at any time; + // this test case only ensures that the corner case is handled reasonably, and that the output is stable + // (i.e. the operator does not cause unnecessary changes in output objects.) + {Source: "source.vendor2.com", Mirrors: []string{"local-mirror2.example.com", "registry2.vendor2.com", "registry1.vendor2.com"}}, }, }, - result: [][]string{ - {"local-mirror.example.com", "registry1.vendor.com", "registry2.vendor.com"}, - {"local-mirror2.example.com", "registry1.vendor2.com", "registry2.vendor2.com"}, + result: []apioperatorsv1alpha1.RepositoryDigestMirrors{ + {Source: "source.vendor.com", Mirrors: []string{"local-mirror.example.com", "registry2.vendor.com"}}, + {Source: "source.vendor2.com", Mirrors: []string{"local-mirror2.example.com", "registry1.vendor2.com", "registry2.vendor2.com"}}, }, }, } { t.Run(c.name, func(t *testing.T) { in := []*apioperatorsv1alpha1.ImageContentSourcePolicy{} - for _, l1 := range c.input { - in1 := apioperatorsv1alpha1.ImageContentSourcePolicy{} - for _, l2 := range l1 { - in1.Spec.RepositoryDigestMirrors = append(in1.Spec.RepositoryDigestMirrors, apioperatorsv1alpha1.RepositoryDigestMirrors{ - Sources: l2, - }) - } - in = append(in, &in1) + for _, rdms := range c.input { + in = append(in, &apioperatorsv1alpha1.ImageContentSourcePolicy{ + Spec: apioperatorsv1alpha1.ImageContentSourcePolicySpec{ + RepositoryDigestMirrors: rdms, + }, + }) } - res, err := disjointOrderedMirrorSets(in) + res, err := mergedMirrorSets(in) if err != nil { t.Errorf("Error %v", err) return @@ -210,9 +225,10 @@ func TestUpdateRegistriesConfig(t *testing.T) { icspRules: []*apioperatorsv1alpha1.ImageContentSourcePolicy{ { Spec: apioperatorsv1alpha1.ImageContentSourcePolicySpec{ - RepositoryDigestMirrors: []apioperatorsv1alpha1.RepositoryDigestMirrors{ - {Sources: []string{"insecure.com/ns-i1", "blocked.com/ns-b1", "unrelated.com/ns-u1"}}, - {Sources: []string{"blocked.com/ns-b/ns2-b", "unrelated.com/ns-u2", "insecure.com/ns-i2"}}, + RepositoryDigestMirrors: []apioperatorsv1alpha1.RepositoryDigestMirrors{ // other.com is neither insecure nor blocked + {Source: "insecure.com/ns-i1", Mirrors: []string{"blocked.com/ns-b1", "other.com/ns-o1"}}, + {Source: "blocked.com/ns-b/ns2-b", Mirrors: []string{"other.com/ns-o2", "insecure.com/ns-i2"}}, + {Source: "other.com/ns-o3", Mirrors: []string{"insecure.com/ns-i2", "blocked.com/ns-b/ns3-b"}}, }, }, }, @@ -227,31 +243,7 @@ func TestUpdateRegistriesConfig(t *testing.T) { Blocked: true, MirrorByDigestOnly: true, Mirrors: []sysregistriesv2.Endpoint{ - {Location: "blocked.com/ns-b/ns2-b"}, - {Location: "unrelated.com/ns-u2"}, - {Location: "insecure.com/ns-i2", Insecure: true}, - }, - }, - { - Endpoint: sysregistriesv2.Endpoint{ - Location: "unrelated.com/ns-u2", - }, - MirrorByDigestOnly: true, - Mirrors: []sysregistriesv2.Endpoint{ - {Location: "blocked.com/ns-b/ns2-b"}, - {Location: "unrelated.com/ns-u2"}, - {Location: "insecure.com/ns-i2", Insecure: true}, - }, - }, - { - Endpoint: sysregistriesv2.Endpoint{ - Location: "insecure.com/ns-i2", - Insecure: true, - }, - MirrorByDigestOnly: true, - Mirrors: []sysregistriesv2.Endpoint{ - {Location: "blocked.com/ns-b/ns2-b"}, - {Location: "unrelated.com/ns-u2"}, + {Location: "other.com/ns-o2"}, {Location: "insecure.com/ns-i2", Insecure: true}, }, }, @@ -263,32 +255,19 @@ func TestUpdateRegistriesConfig(t *testing.T) { }, MirrorByDigestOnly: true, Mirrors: []sysregistriesv2.Endpoint{ - {Location: "insecure.com/ns-i1", Insecure: true}, {Location: "blocked.com/ns-b1"}, - {Location: "unrelated.com/ns-u1"}, - }, - }, - { - Endpoint: sysregistriesv2.Endpoint{ - Location: "blocked.com/ns-b1", - }, - Blocked: true, - MirrorByDigestOnly: true, - Mirrors: []sysregistriesv2.Endpoint{ - {Location: "insecure.com/ns-i1", Insecure: true}, - {Location: "blocked.com/ns-b1"}, - {Location: "unrelated.com/ns-u1"}, + {Location: "other.com/ns-o1"}, }, }, + { Endpoint: sysregistriesv2.Endpoint{ - Location: "unrelated.com/ns-u1", + Location: "other.com/ns-o3", }, MirrorByDigestOnly: true, Mirrors: []sysregistriesv2.Endpoint{ - {Location: "insecure.com/ns-i1", Insecure: true}, - {Location: "blocked.com/ns-b1"}, - {Location: "unrelated.com/ns-u1"}, + {Location: "insecure.com/ns-i2", Insecure: true}, + {Location: "blocked.com/ns-b/ns3-b"}, }, }, diff --git a/pkg/controller/container-runtime-config/union.go b/pkg/controller/container-runtime-config/union.go deleted file mode 100644 index 11c729efbd..0000000000 --- a/pkg/controller/container-runtime-config/union.go +++ /dev/null @@ -1,84 +0,0 @@ -package containerruntimeconfig - -// unionNode is a node in the union graph. In our case, it is a mirror host[:port]. -type unionNode = string // An alias, so that the users don't have to explicitly cast data. - -// internalUnionNode is the unionGraph representation of a node. -// There is exactly one instance of *internalUnionNode for each .public value. -type internalUnionNode struct { - public unionNode - parent *internalUnionNode // A higher-ranking member of the same disjoint set, or nil - rank int -} - -// unionGraph is a set of unionNodes which supports identifying disjoint subsets. -// See e.g. https://en.wikipedia.org/wiki/Disjoint-set_data_structure . -// The graph is built implicitly, i.e. there is no explicit “add node” operation; it is perfectly valid -// to just call .Union or .Find using unionNode values that have never been mentioned before. -type unionGraph struct { - // This is not all that efficient, the hash map lookups by unionNode == string are likely more costly than any other - // use of the graph, but the graphs we need to handle are very small and readability is more important. - nodes map[unionNode]*internalUnionNode -} - -// newUnionGraph returns an empty unionGraph. -func newUnionGraph() *unionGraph { - return &unionGraph{ - nodes: map[unionNode]*internalUnionNode{}, - } -} - -// getNode returns g.nodes[node], creating it if it does not exist yet. -func (g *unionGraph) getNode(node unionNode) *internalUnionNode { - res, ok := g.nodes[node] - if !ok { - res = &internalUnionNode{ - public: node, - parent: nil, - rank: 0, - } - g.nodes[node] = res - } - return res -} - -// find is Find(), except it returns a *internalUnionNode -func (g *unionGraph) find(query unionNode) *internalUnionNode { - queryNode := g.getNode(query) - // Find root - x := queryNode - for x.parent != nil { - x = x.parent - } - root := x - // Compress path - x = queryNode - for x != root { - next := x.parent - x.parent = root - x = next - } - return root -} - -// Find returns a representative member of the disjoint set containing query. -// All Find() calls on members of the same disjoint set return the same representative member until g.Union is called again. -func (g *unionGraph) Find(query unionNode) unionNode { - return g.find(query).public -} - -// Union joins the sets of a, b -func (g *unionGraph) Union(a, b unionNode) { - inA := g.find(a) - inB := g.find(b) - if inA != inB { - if inA.rank > inB.rank { - inB.parent = inA - } else if inA.rank < inB.rank { - inA.parent = inB - } else { // inA.rank == inB.rank - inA.parent = inB - inB.rank++ - } - } -} diff --git a/pkg/controller/container-runtime-config/union_test.go b/pkg/controller/container-runtime-config/union_test.go deleted file mode 100644 index 127e0f78bb..0000000000 --- a/pkg/controller/container-runtime-config/union_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package containerruntimeconfig - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestUnionGraph(t *testing.T) { - for _, c := range []struct { - name string - unions []string // Each string represents as a pair of unionNode values - result []string // Each string represents a set of unionNode values - }{ - {name: "Empty", unions: []string{}, result: []string{}}, - { - name: "No unions", // With no Union calls, still behaves correctly for individual nodes - unions: []string{}, - result: []string{"A", "B", "C"}, - }, - { - name: "Self-unions", - unions: []string{"AA", "BB", "CC"}, - result: []string{"A", "B", "C"}, - }, - { - name: "Connected: common first element", - unions: []string{"AB", "AC", "AD", "AE", "ab", "ac", "ad", "ae"}, - result: []string{"ABCDE", "abcde"}, - }, - { - name: "Connected: common second element", - unions: []string{"BA", "CA", "DA", "EA", "ba", "ca", "da", "ea"}, - result: []string{"ABCDE", "abcde"}, - }, - { - name: "Connected: path", - unions: []string{"AB", "BC", "CD", "DE", "ab", "bc", "cd", "de"}, - result: []string{"ABCDE", "abcde"}, - }, - } { - t.Run(c.name, func(t *testing.T) { - // Verify various rotations of the input to test the behavior is order-independent - for unionOffset := 0; unionOffset == 0 || unionOffset < len(c.unions); unionOffset++ { - for resultOffset := 0; resultOffset < len(c.result); resultOffset++ { - // We could use t.Run() for each offset pair pass, but that would pollute the output too much for developers that - // want to inspect other tests. - t.Logf("Pass %d,%d", unionOffset, resultOffset) - g := newUnionGraph() - for i := 0; i < len(c.unions); i++ { - u := c.unions[(unionOffset+i)%len(c.unions)] - if len(u) != 2 { - panic("Invalid unions element") - } - g.Union(u[0:1], u[1:2]) - } - for i := 0; i < len(c.result); i++ { - res := c.result[(resultOffset+i)%len(c.result)] - expected := g.Find(res[0:1]) - require.Contains(t, res, expected) - for j := 1; j < len(res); j++ { - v := g.Find(res[j : j+1]) - assert.Equal(t, expected, v) - } - // Check that the root originally returned for res[0:1] did not change by querying the members of the set - v := g.Find(res[0:1]) - assert.Equal(t, expected, v) - } - } - } - }) - } -} diff --git a/vendor/github.com/openshift/api/operator/v1/types_network.go b/vendor/github.com/openshift/api/operator/v1/types_network.go index b01127bd2c..cdc3fd0f97 100644 --- a/vendor/github.com/openshift/api/operator/v1/types_network.go +++ b/vendor/github.com/openshift/api/operator/v1/types_network.go @@ -221,6 +221,10 @@ type OpenShiftSDNConfig struct { // it will be provided separately. If set, you must provide it yourself. // +optional UseExternalOpenvswitch *bool `json:"useExternalOpenvswitch,omitempty"` + + // enableUnidling controls whether or not the service proxy will support idling + // and unidling of services. By default, unidling is enabled. + EnableUnidling *bool `json:"enableUnidling,omitempty"` } // KuryrConfig configures the Kuryr-Kubernetes SDN diff --git a/vendor/github.com/openshift/api/operator/v1/zz_generated.deepcopy.go b/vendor/github.com/openshift/api/operator/v1/zz_generated.deepcopy.go index 23b8c53af3..78b3f926da 100644 --- a/vendor/github.com/openshift/api/operator/v1/zz_generated.deepcopy.go +++ b/vendor/github.com/openshift/api/operator/v1/zz_generated.deepcopy.go @@ -1507,6 +1507,11 @@ func (in *OpenShiftSDNConfig) DeepCopyInto(out *OpenShiftSDNConfig) { *out = new(bool) **out = **in } + if in.EnableUnidling != nil { + in, out := &in.EnableUnidling, &out.EnableUnidling + *out = new(bool) + **out = **in + } return } diff --git a/vendor/github.com/openshift/api/operator/v1/zz_generated.swagger_doc_generated.go b/vendor/github.com/openshift/api/operator/v1/zz_generated.swagger_doc_generated.go index 723d839627..feee6b5480 100644 --- a/vendor/github.com/openshift/api/operator/v1/zz_generated.swagger_doc_generated.go +++ b/vendor/github.com/openshift/api/operator/v1/zz_generated.swagger_doc_generated.go @@ -464,6 +464,7 @@ var map_OpenShiftSDNConfig = map[string]string{ "vxlanPort": "vxlanPort is the port to use for all vxlan packets. The default is 4789.", "mtu": "mtu is the mtu to use for the tunnel interface. Defaults to 1450 if unset. This must be 50 bytes smaller than the machine's uplink.", "useExternalOpenvswitch": "useExternalOpenvswitch tells the operator not to install openvswitch, because it will be provided separately. If set, you must provide it yourself.", + "enableUnidling": "enableUnidling controls whether or not the service proxy will support idling and unidling of services. By default, unidling is enabled.", } func (OpenShiftSDNConfig) SwaggerDoc() map[string]string { diff --git a/vendor/github.com/openshift/api/operator/v1alpha1/types_image_content_source_policy.go b/vendor/github.com/openshift/api/operator/v1alpha1/types_image_content_source_policy.go index 26eeef841a..db5ec6ab15 100644 --- a/vendor/github.com/openshift/api/operator/v1alpha1/types_image_content_source_policy.go +++ b/vendor/github.com/openshift/api/operator/v1alpha1/types_image_content_source_policy.go @@ -7,7 +7,7 @@ import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // ImageContentSourcePolicy holds cluster-wide information about how to handle registry mirror rules. -// When multple policies are defined, the outcome of the behavior is defined on each field. +// When multiple policies are defined, the outcome of the behavior is defined on each field. type ImageContentSourcePolicy struct { metav1.TypeMeta `json:",inline"` // Standard object's metadata. @@ -24,14 +24,19 @@ type ImageContentSourcePolicySpec struct { // repositoryDigestMirrors allows images referenced by image digests in pods to be // pulled from alternative mirrored repository locations. The image pull specification // provided to the pod will be compared to the source locations described in RepositoryDigestMirrors - // and the image may be pulled down from any of the repositories in the list instead of the + // and the image may be pulled down from any of the mirrors in the list instead of the // specified repository allowing administrators to choose a potentially faster mirror. // Only image pull specifications that have an image disgest will have this behavior applied // to them - tags will continue to be pulled from the specified repository in the pull spec. - // When multiple policies are defined, any overlaps found will be merged together when the mirror - // rules are written to `/etc/containers/registries.conf`. For example, if policy A has sources `a, b, c` - // and policy B has sources `c, d, e`. Then the mirror rule written to `registries.conf` will be `a, b, c, d, e` - // where the duplicate `c` is removed. + // + // Each “source” repository is treated independently; configurations for different “source” + // repositories don’t interact. + // + // When multiple policies are defined for the same “source” repository, the sets of defined + // mirrors will be merged together, preserving the relative order of the mirrors, if possible. + // For example, if policy A has mirrors `a, b, c` and policy B has mirrors `c, d, e`, the + // mirrors will be used in the order `a, b, c, d, e`. If the orders of mirror entries conflict + // (e.g. `a, b` vs. `b, a`) the configuration is not rejected but the resulting order is unspecified. // +optional RepositoryDigestMirrors []RepositoryDigestMirrors `json:"repositoryDigestMirrors"` } @@ -47,9 +52,17 @@ type ImageContentSourcePolicyList struct { } // RepositoryDigestMirrors holds cluster-wide information about how to handle mirros in the registries config. -// Note: the mirrors only work when pulling the images that are reference by their digests. +// Note: the mirrors only work when pulling the images that are referenced by their digests. type RepositoryDigestMirrors struct { - // sources are repositories that are mirrors of each other. + // source is the repository that users refer to, e.g. in image pull specifications. + // +required + Source string `json:"source"` + // mirrors is one or more repositories that may also contain the same images. + // The order of mirrors in this list is treated as the user's desired priority, while source + // is by default considered lower priority than all mirrors. Other cluster configuration, + // including (but not limited to) other repositoryDigestMirrors objects, + // may impact the exact order mirrors are contacted in, or some mirrors may be contacted + // in parallel, so this should be considered a preference rather than a guarantee of ordering. // +optional - Sources []string `json:"sources,omitempty"` + Mirrors []string `json:"mirrors"` } diff --git a/vendor/github.com/openshift/api/operator/v1alpha1/zz_generated.deepcopy.go b/vendor/github.com/openshift/api/operator/v1alpha1/zz_generated.deepcopy.go index 36a17cff8b..8a1bc1d2f8 100644 --- a/vendor/github.com/openshift/api/operator/v1alpha1/zz_generated.deepcopy.go +++ b/vendor/github.com/openshift/api/operator/v1alpha1/zz_generated.deepcopy.go @@ -275,8 +275,8 @@ func (in *OperatorStatus) DeepCopy() *OperatorStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RepositoryDigestMirrors) DeepCopyInto(out *RepositoryDigestMirrors) { *out = *in - if in.Sources != nil { - in, out := &in.Sources, &out.Sources + if in.Mirrors != nil { + in, out := &in.Mirrors, &out.Mirrors *out = make([]string, len(*in)) copy(*out, *in) } diff --git a/vendor/github.com/openshift/api/operator/v1alpha1/zz_generated.swagger_doc_generated.go b/vendor/github.com/openshift/api/operator/v1alpha1/zz_generated.swagger_doc_generated.go index fc80a126b8..84d04215c7 100644 --- a/vendor/github.com/openshift/api/operator/v1alpha1/zz_generated.swagger_doc_generated.go +++ b/vendor/github.com/openshift/api/operator/v1alpha1/zz_generated.swagger_doc_generated.go @@ -136,7 +136,7 @@ func (VersionAvailability) SwaggerDoc() map[string]string { } var map_ImageContentSourcePolicy = map[string]string{ - "": "ImageContentSourcePolicy holds cluster-wide information about how to handle registry mirror rules. When multple policies are defined, the outcome of the behavior is defined on each field.", + "": "ImageContentSourcePolicy holds cluster-wide information about how to handle registry mirror rules. When multiple policies are defined, the outcome of the behavior is defined on each field.", "metadata": "Standard object's metadata.", "spec": "spec holds user settable values for configuration", } @@ -156,7 +156,7 @@ func (ImageContentSourcePolicyList) SwaggerDoc() map[string]string { var map_ImageContentSourcePolicySpec = map[string]string{ "": "ImageContentSourcePolicySpec is the specification of the ImageContentSourcePolicy CRD.", - "repositoryDigestMirrors": "repositoryDigestMirrors allows images referenced by image digests in pods to be pulled from alternative mirrored repository locations. The image pull specification provided to the pod will be compared to the source locations described in RepositoryDigestMirrors and the image may be pulled down from any of the repositories in the list instead of the specified repository allowing administrators to choose a potentially faster mirror. Only image pull specifications that have an image disgest will have this behavior applied to them - tags will continue to be pulled from the specified repository in the pull spec. When multiple policies are defined, any overlaps found will be merged together when the mirror rules are written to `/etc/containers/registries.conf`. For example, if policy A has sources `a, b, c` and policy B has sources `c, d, e`. Then the mirror rule written to `registries.conf` will be `a, b, c, d, e` where the duplicate `c` is removed.", + "repositoryDigestMirrors": "repositoryDigestMirrors allows images referenced by image digests in pods to be pulled from alternative mirrored repository locations. The image pull specification provided to the pod will be compared to the source locations described in RepositoryDigestMirrors and the image may be pulled down from any of the mirrors in the list instead of the specified repository allowing administrators to choose a potentially faster mirror. Only image pull specifications that have an image disgest will have this behavior applied to them - tags will continue to be pulled from the specified repository in the pull spec.\n\nEach “source” repository is treated independently; configurations for different “source” repositories don’t interact.\n\nWhen multiple policies are defined for the same “source” repository, the sets of defined mirrors will be merged together, preserving the relative order of the mirrors, if possible. For example, if policy A has mirrors `a, b, c` and policy B has mirrors `c, d, e`, the mirrors will be used in the order `a, b, c, d, e`. If the orders of mirror entries conflict (e.g. `a, b` vs. `b, a`) the configuration is not rejected but the resulting order is unspecified.", } func (ImageContentSourcePolicySpec) SwaggerDoc() map[string]string { @@ -164,8 +164,9 @@ func (ImageContentSourcePolicySpec) SwaggerDoc() map[string]string { } var map_RepositoryDigestMirrors = map[string]string{ - "": "RepositoryDigestMirrors holds cluster-wide information about how to handle mirros in the registries config. Note: the mirrors only work when pulling the images that are reference by their digests.", - "sources": "sources are repositories that are mirrors of each other.", + "": "RepositoryDigestMirrors holds cluster-wide information about how to handle mirros in the registries config. Note: the mirrors only work when pulling the images that are referenced by their digests.", + "source": "source is the repository that users refer to, e.g. in image pull specifications.", + "mirrors": "mirrors is one or more repositories that may also contain the same images. The order of mirrors in this list is treated as the user's desired priority, while source is by default considered lower priority than all mirrors. Other cluster configuration, including (but not limited to) other repositoryDigestMirrors objects, may impact the exact order mirrors are contacted in, or some mirrors may be contacted in parallel, so this should be considered a preference rather than a guarantee of ordering.", } func (RepositoryDigestMirrors) SwaggerDoc() map[string]string {