diff --git a/go.mod b/go.mod index ed09a0647..1acfc5a86 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/google/go-cmp v0.3.1 github.com/kevinburke/go-bindata v3.11.0+incompatible - github.com/openshift/api v0.0.0-20200416222006-8cfd79130f9c + github.com/openshift/api v0.0.0-20200522173408-17ada6e4245b github.com/openshift/library-go v0.0.0-20200324092245-db2a8546af81 github.com/pkg/errors v0.8.1 @@ -28,8 +28,8 @@ require ( gopkg.in/fsnotify.v1 v1.4.7 gopkg.in/yaml.v2 v2.2.8 - k8s.io/api v0.18.0 - k8s.io/apimachinery v0.18.0 + k8s.io/api v0.18.3 + k8s.io/apimachinery v0.18.3 k8s.io/apiserver v0.18.0 k8s.io/client-go v0.18.0 diff --git a/go.sum b/go.sum index 40ea7c327..f823d344d 100644 --- a/go.sum +++ b/go.sum @@ -288,9 +288,10 @@ github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQ github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.0.0-20191031171055-b133feaeeb2e/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/openshift/api v0.0.0-20200320142426-0de0d539b0c3/go.mod h1:7k3+uZYOir97walbYUqApHUA2OPhkQpVJHt0n7GJ6P4= -github.com/openshift/api v0.0.0-20200416222006-8cfd79130f9c h1:lpLa1z9GCxcpHsfHq7ETFdOthnyy1PlD7mvDzVrenjc= -github.com/openshift/api v0.0.0-20200416222006-8cfd79130f9c/go.mod h1:RKMJ5CBnljLfnej+BJ/xnOWc3kZDvJUaIAEq2oKSPtE= +github.com/openshift/api v0.0.0-20200522173408-17ada6e4245b h1:yEY4zF6pEEWdR09wjuDJhupLEDM2vYdUgLoc10C0fFk= +github.com/openshift/api v0.0.0-20200522173408-17ada6e4245b/go.mod h1:TkhafijfTiRi1Q3120/ZSE4oIWKQ4DGRh3byPywv4Mw= github.com/openshift/build-machinery-go v0.0.0-20200211121458-5e3d6e570160/go.mod h1:1CkcsT3aVebzRBzVTSbiKSkJMsC/CASqxesfqEMfJEc= +github.com/openshift/build-machinery-go v0.0.0-20200424080330-082bf86082cc/go.mod h1:1CkcsT3aVebzRBzVTSbiKSkJMsC/CASqxesfqEMfJEc= github.com/openshift/client-go v0.0.0-20200320150128-a906f3d8e723/go.mod h1:wNBSSt4RZTHhUWyhBE3gxTR32QpF9DB2SfS14u2IxuE= github.com/openshift/library-go v0.0.0-20200324092245-db2a8546af81 h1:bNUcSdyoACkjI2USyvDbAMb6lCtghdz563b0bfhPC8A= github.com/openshift/library-go v0.0.0-20200324092245-db2a8546af81/go.mod h1:Qc5duoXHzAKyUfA0REIlG/rdfWzknOpp9SiDiyg5Y7A= @@ -525,6 +526,8 @@ k8s.io/api v0.18.0-rc.1 h1:hEUIbswR8BaacroH6OX/rjrK1kvEIUFZt3EpEar7/CM= k8s.io/api v0.18.0-rc.1/go.mod h1:ZOh6SbHjOYyaMLlWmB2+UOQKEWDpCnVEVpEyt7S2J9s= k8s.io/api v0.18.0 h1:lwYk8Vt7rsVTwjRU6pzEsa9YNhThbmbocQlKvNBB4EQ= k8s.io/api v0.18.0/go.mod h1:q2HRQkfDzHMBZL9l/y9rH63PkQl4vae0xRT+8prbrK8= +k8s.io/api v0.18.3 h1:2AJaUQdgUZLoDZHrun21PW2Nx9+ll6cUzvn3IKhSIn0= +k8s.io/api v0.18.3/go.mod h1:UOaMwERbqJMfeeeHc8XJKawj4P9TgDRnViIqqBeH2QA= k8s.io/apiextensions-apiserver v0.18.0-beta.2/go.mod h1:Hnrg5jx8/PbxRbUoqDGxtQkULjwx8FDW4WYJaKNK+fk= k8s.io/apiextensions-apiserver v0.18.0-rc.1 h1:rhdw261gFouyOz8A8G0J1bhpOF2lLUF+LWv1o0d81E8= k8s.io/apiextensions-apiserver v0.18.0-rc.1/go.mod h1:U5F8MzX2yXVHRuSKY5xBkS9999/VHu6PJNOInIpVLe4= @@ -535,6 +538,8 @@ k8s.io/apimachinery v0.18.0-rc.1 h1:pYWMpBgJdEe1OacuI89D49JLcTG5ZLx9D7a1dGOdYh4= k8s.io/apimachinery v0.18.0-rc.1/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= k8s.io/apimachinery v0.18.0 h1:fuPfYpk3cs1Okp/515pAf0dNhL66+8zk8RLbSX+EgAE= k8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= +k8s.io/apimachinery v0.18.3 h1:pOGcbVAhxADgUYnjS08EFXs9QMl8qaH5U4fr5LGUrSk= +k8s.io/apimachinery v0.18.3/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko= k8s.io/apiserver v0.18.0-beta.2/go.mod h1:bnblMkMoCFnIfVnVftd0SXJPzyvrk3RtaqSbblphF/A= k8s.io/apiserver v0.18.0-rc.1 h1:P+tvb0Hum3tsyz6cF/mKJwXhDB/DMGhkkx+K7MQWj04= k8s.io/apiserver v0.18.0-rc.1/go.mod h1:RYE9w2Lijk1aWW3i3pS7kFGU0Afof+UDoOz1qW9aSYg= @@ -548,6 +553,7 @@ k8s.io/client-go v0.18.0/go.mod h1:uQSYDYs4WhVZ9i6AIoEZuwUggLVEF64HOD37boKAtF8= k8s.io/code-generator v0.18.0-beta.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= k8s.io/code-generator v0.18.0-rc.1/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= k8s.io/code-generator v0.18.0/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= +k8s.io/code-generator v0.18.3/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c= k8s.io/component-base v0.18.0-beta.2/go.mod h1:HVk5FpRnyzQ/MjBr9//e/yEBjTVa2qjGXCTuUzcD7ks= k8s.io/component-base v0.18.0-rc.1/go.mod h1:NNlRaxZEdLqTs2+6yXiU2SHl8gKsbcy19Ii+Sfq53RM= k8s.io/component-base v0.18.0/go.mod h1:u3BCg0z1uskkzrnAKFzulmYaEpZF7XC9Pf/uFyb1v2c= @@ -560,6 +566,8 @@ k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/kube-aggregator v0.18.0-beta.2/go.mod h1:O3Td9mheraINbLHH4pzoFP2gRzG0Wk1COqzdSL4rBPk= k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c h1:/KUFqjjqAcY4Us6luF5RDNZ16KJtb49HfR3ZHB9qYXM= k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= +k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6 h1:Oh3Mzx5pJ+yIumsAD0MOECPVeXsVot0UkiaCGVyfGQY= +k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= k8s.io/utils v0.0.0-20200229041039-0a110f9eb7ab h1:I3f2hcBrepGRXI1z4sukzAb8w1R4eqbsHrAsx06LGYM= k8s.io/utils v0.0.0-20200229041039-0a110f9eb7ab/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU= diff --git a/manifests/00-custom-resource-definition-internal.yaml b/manifests/00-custom-resource-definition-internal.yaml index 81ddf111c..59a304757 100644 --- a/manifests/00-custom-resource-definition-internal.yaml +++ b/manifests/00-custom-resource-definition-internal.yaml @@ -69,6 +69,15 @@ spec: description: status is the most recently observed status of the dnsRecord. type: object properties: + observedGeneration: + description: observedGeneration is the most recently observed generation + of the DNSRecord. When the DNSRecord is updated, the controller updates + the corresponding record in each managed zone. If an update for a + particular zone fails, that failure is recorded in the status condition + for the zone so that the controller can determine that it needs to + retry the update for that specific zone. + type: integer + format: int64 zones: description: zones are the status of the record in each zone. type: array diff --git a/pkg/dns/aws/dns.go b/pkg/dns/aws/dns.go index 989a19737..7c75273c9 100644 --- a/pkg/dns/aws/dns.go +++ b/pkg/dns/aws/dns.go @@ -22,7 +22,6 @@ import ( "github.com/aws/aws-sdk-go/service/route53" kerrors "k8s.io/apimachinery/pkg/util/errors" - "k8s.io/apimachinery/pkg/util/sets" configv1 "github.com/openshift/api/config/v1" ) @@ -56,12 +55,6 @@ type Provider struct { // lbZones is a cache of load balancer DNS names to LB hosted zone IDs. lbZones map[string]string - - // updatedRecords is a cache of records which have been created or updated - // during the life of this manager. The key is zoneID+domain+target. This is a - // quick hack to minimize AWS API calls, and also prevent changes to existing - // records (something not yet supported). - updatedRecords sets.String } // Config is the necessary input to configure the manager. @@ -119,13 +112,12 @@ func NewProvider(config Config, operatorReleaseVersion string) (*Provider, error } return &Provider{ - elb: elb.New(sess, aws.NewConfig().WithRegion(region)), - route53: route53.New(sess, r53Config), - tags: resourcegroupstaggingapi.New(sess, tagConfig), - config: config, - idsToTags: map[string]map[string]string{}, - lbZones: map[string]string{}, - updatedRecords: sets.NewString(), + elb: elb.New(sess, aws.NewConfig().WithRegion(region)), + route53: route53.New(sess, r53Config), + tags: resourcegroupstaggingapi.New(sess, tagConfig), + config: config, + idsToTags: map[string]map[string]string{}, + lbZones: map[string]string{}, }, nil } @@ -275,26 +267,15 @@ func (m *Provider) change(record *iov1.DNSRecord, zone configv1.DNSZone, action return fmt.Errorf("failed to get hosted zone for load balancer target %q: %v", target, err) } - // Configure records and cache updates. - // TODO: handle the caching/diff detection in a better way. - m.lock.Lock() - defer m.lock.Unlock() - key := zoneID + domain + target - // Only process updates once for now because we're not diffing. - if m.updatedRecords.Has(key) && action == upsertAction { - log.V(2).Info("skipping cached DNS record update", "record", record) - return nil - } + // Configure records. err = m.updateAlias(domain, zoneID, target, targetHostedZoneID, string(action)) if err != nil { return fmt.Errorf("failed to update alias in zone %s: %v", zoneID, err) } switch action { case upsertAction: - m.updatedRecords.Insert(key) log.Info("upserted DNS record", "record", record) case deleteAction: - m.updatedRecords.Delete(key) log.Info("deleted DNS record", "record", record) } return nil diff --git a/pkg/manifests/bindata.go b/pkg/manifests/bindata.go index d57ebb9de..22ea94232 100644 --- a/pkg/manifests/bindata.go +++ b/pkg/manifests/bindata.go @@ -12,7 +12,7 @@ // assets/router/service-cloud.yaml (631B) // assets/router/service-internal.yaml (429B) // manifests/00-cluster-role.yaml (2.411kB) -// manifests/00-custom-resource-definition-internal.yaml (5.796kB) +// manifests/00-custom-resource-definition-internal.yaml (6.371kB) // manifests/00-custom-resource-definition.yaml (42.103kB) // manifests/00-ingress-credentials-request.yaml (1.464kB) // manifests/00-namespace.yaml (266B) @@ -336,7 +336,7 @@ func manifests00ClusterRoleYaml() (*asset, error) { return a, nil } -var _manifests00CustomResourceDefinitionInternalYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x58\x51\x6f\xdc\xc8\x0d\x7e\xf7\xaf\x20\x36\x0f\xf7\x62\x69\xcf\x97\xb6\x28\x16\x28\x8a\xc0\xb9\x14\x46\x73\x6e\x10\xbb\x29\xd0\xc4\xc0\xcd\x6a\x28\x89\x17\x69\x46\x37\x1c\xad\x6f\x5d\xf4\xbf\x17\x9c\x91\xb4\xd2\xae\xb4\x36\x1a\xdc\x3c\xed\x72\x28\xf2\xe3\x47\x0e\x87\x92\x6a\xe8\x13\x3a\x26\x6b\x36\xa0\x1a\xc2\xdf\x3c\x1a\xf9\xc7\xe9\xd7\x3f\x73\x4a\x76\xbd\xbb\xda\xa2\x57\x57\x17\x5f\xc9\xe8\x0d\x5c\xb7\xec\x6d\xfd\x11\xd9\xb6\x2e\xc3\xb7\x98\x93\x21\x4f\xd6\x5c\xd4\xe8\x95\x56\x5e\x6d\x2e\x00\x8c\xaa\x71\x03\xda\xb0\xc3\xcc\x3a\xcd\x29\x99\xc2\x21\x73\x6a\x1b\x74\xca\x5b\x27\x3f\x0c\x97\x94\xfb\x94\xec\x05\x37\x98\xc9\x63\x85\xb3\x6d\xb3\x81\xf3\xca\xd1\x3a\x8b\x3e\x40\xc4\xf4\xf6\xf6\xee\x63\x70\x14\x64\x15\xb1\xff\xfb\x54\xfe\x9e\xd8\x87\xbd\xa6\x6a\x9d\xaa\xc6\xd0\x82\x98\xc9\x14\x6d\xa5\xdc\x68\xe3\x02\x80\x33\xdb\xe0\x06\x56\xab\x0b\x80\xc6\x21\xa3\xdb\xe1\x3f\xcd\x57\x63\x1f\xcd\x3b\xc2\x4a\xf3\x06\x72\x55\x31\x8a\x6a\xbb\x75\x1d\x25\x1d\x32\xf6\xca\xb7\xbc\x81\xff\xfc\xf7\x02\x60\xa7\x2a\xd2\x4a\x68\x8a\x9b\x12\xd0\x9b\x0f\x37\x9f\x5e\xdf\x65\x25\xd6\x2a\x0a\x01\x34\x72\xe6\xa8\x09\x7a\xb0\x1a\xd0\x03\x31\x28\x09\x06\x22\x34\xa8\x95\x51\x05\x6a\x20\x03\xbe\x44\x78\xb2\x06\x19\xb4\xa4\x02\x35\x6c\xf7\x12\x44\x9a\x59\x93\x53\x31\xa1\x6e\x9d\x55\x2d\x7b\x74\x9d\x37\x80\x54\x88\x4f\x9b\x76\x5b\x51\xf6\x6f\x6b\x10\x94\xd1\xbd\xd0\xd1\x4e\x79\x14\x69\x0a\x5f\x0c\x5c\xc7\x47\x41\xe9\x9a\x8c\x00\xa0\xa6\xad\x42\x44\x60\x73\xf0\x25\xf1\x60\xb5\x27\x42\x60\x1b\xeb\x81\xdb\xa6\xb1\xce\xa3\x4e\xe1\xbe\x24\x9e\xec\x5b\x53\xed\x21\xb7\x0e\xc8\x78\x74\x46\x55\x90\xd9\xba\x6e\x0d\x65\xc1\xf6\x60\xd3\xe6\xf0\x8f\x06\xcd\x9d\x44\x02\x7d\x5d\x70\xba\xea\x14\xfc\x5e\x12\x65\xb7\xbf\x60\xe6\x3b\x51\xe3\x44\xcd\x53\x9f\x0f\x59\xa3\x52\x1f\x64\x47\xac\x7f\x27\x69\x89\x3a\x1d\xa3\x1c\x38\xde\x45\x19\x6a\xe0\x90\xb2\x3e\x6a\x70\x18\x4a\xc3\xf8\x29\xe0\x0e\xb4\x32\x1d\xaa\x14\xee\xa4\x7c\x1c\x03\x97\xb6\xad\x34\x64\xd6\xec\xd0\xf9\x90\xd2\xc2\xd0\xd3\x60\x99\xc1\xdb\xe0\xb2\x52\x1e\xd9\x4f\x2c\x0e\x2c\xed\x54\xd5\xe2\x65\xc8\x57\xad\xf6\xe0\x50\x7c\x40\x6b\x46\xd6\x82\x0a\xa7\xf0\x93\x75\x08\x64\x72\xbb\x81\xd2\xfb\x86\x37\xeb\x75\x41\xbe\x3f\xdc\x1d\xdd\x7e\xbf\xce\xac\xf1\x8e\xb6\xad\x10\xbb\xd6\xb8\xc3\x6a\xcd\x54\x24\xca\x65\x25\x79\xcc\x7c\xeb\x70\xad\x1a\x4a\x02\x70\xe3\x43\x87\xa8\xf5\xab\xa1\xec\xbf\x1b\x21\x8d\xf9\x60\xef\xc8\x14\x83\x38\x9c\xd5\x45\xde\xe5\xc4\xc6\x42\x8f\x8f\x45\xfc\x07\x7a\x45\x24\xac\x7c\xfc\xf1\xee\xfe\x50\x42\x93\xc2\x0b\x9c\x07\xb6\x0f\x8f\xf1\x81\x78\x21\x8a\x4c\x8e\x2e\x26\x2e\x77\xb6\x0e\x16\xd1\xe8\xc6\x92\xf1\xe1\x4f\x56\x11\x9a\x29\xe9\xdc\x6e\x6b\xf2\x92\xe9\x5f\x5b\x64\x2f\xf9\x49\xe1\x5a\x19\xa9\xed\x2d\x42\xdb\x68\x15\x8a\xfb\xc6\xc0\xb5\xaa\xb1\xba\x56\x8c\xbf\x3b\xed\xc2\x30\x27\x42\xe9\xf3\xc4\x8f\x3b\xf3\x54\x71\x72\x62\x00\xfa\x3e\x3c\x9b\x21\xd9\x94\x04\x09\x4b\xf2\x9b\xf2\xee\x90\xc6\xa3\x80\xa2\x4d\x4e\x1a\x10\x96\x6a\x47\xd6\x0d\x72\xc3\xa1\x6d\xa5\xcf\xb9\x87\x40\xb1\xd8\x18\x83\x48\xc4\xc0\xad\xaa\x71\x22\x8b\x9d\xf0\xfe\xfe\xfd\x9c\x74\xdf\x4c\x95\xbd\x72\x05\xfa\x71\xa1\xcc\x75\x87\x10\x71\x74\x35\x15\x1e\x11\xd1\xe9\xf4\x5c\x94\x96\xbd\xdc\x48\x7d\xb8\x87\x3e\x7d\x64\x64\x36\x37\x5d\x86\xc8\xbc\x47\x53\xf8\x72\x03\x57\x93\xad\x21\xcc\xb3\x88\x06\xad\x1e\x53\x77\x4f\x04\x89\x01\xa1\xdf\x68\x4e\xe1\x26\x87\x27\x74\xf6\xb2\x4b\x57\xae\xda\xca\x1f\xd9\x05\x31\xf1\xfa\xfb\x74\x16\xbb\xb4\x9f\x62\x74\x7f\xc4\x95\x5b\x57\x2b\x1f\x76\xff\xf4\x87\xd3\xc0\xa8\x6e\xeb\x0d\x7c\x3f\x17\x96\x18\x7d\x41\x5c\xfb\x66\x20\x7b\x74\x09\x0a\xa4\x14\xde\x59\x07\xf8\x9b\xaa\x9b\x0a\x2f\x61\xf5\x66\x05\xf6\x18\x1e\xc0\xea\xfa\xf6\xcd\x4f\x3f\xae\xe6\x63\x9a\xcd\x07\x9a\xb6\x3e\x46\x96\x40\x30\x73\x22\x7d\x33\x91\x74\xa5\x76\x36\xac\x4e\x07\x94\x1b\x32\xd5\x89\xe6\x21\x2a\xe7\xd4\xfe\x94\xd8\x1b\x8f\x35\x1f\x17\x0c\x00\x05\xf1\x09\x09\xb3\xd1\x76\x43\xca\xe2\x91\x0f\xdb\x3d\xf7\xb5\xe5\x70\x5d\xa1\xf1\xd5\x1e\xec\x36\xcc\x43\xba\x57\x3a\x1c\xf6\x8f\x2f\x3d\xed\x4b\x87\x30\xcc\x33\x67\x19\x8c\x13\x8f\xf0\x17\xba\xd1\x04\x41\xc7\x28\x19\x40\x95\x95\x41\xf5\xe5\xb4\x2e\x90\x37\x71\xfe\xf6\xf6\x4e\xc6\xa2\xbb\x09\x39\x07\x0c\xaa\x47\xf0\x48\xbe\x24\x23\x17\x5a\xd7\x2d\x4f\xcc\xc2\x2c\xba\x45\xbe\xce\xb3\x16\x97\x9c\xf4\x30\x8f\xcf\xee\x1e\x8f\x98\x07\xed\x40\xa6\x32\x7b\x18\x8b\x98\x6d\x46\x72\xb9\x85\x58\x24\xce\x59\x9b\x30\xe2\xbc\x9f\x47\xc3\xd0\x78\x93\x43\x18\x2e\xb9\xec\x6f\xef\x4e\x31\x57\x54\x71\x6c\x43\x5f\x56\xef\x14\x55\xa8\xbf\xac\x16\x6c\x0f\x80\xe0\x91\xaa\x4a\x6e\x5c\x46\x1f\x01\x09\xd5\x8a\xad\x89\x73\x10\x32\xab\x02\xbb\x08\xb7\x9d\xc7\x25\xa3\xaa\xe5\xa1\x63\x0b\x9a\xd6\x61\x3a\x8f\x60\xb9\x58\xe2\x5a\x28\x99\x19\xba\xbb\xc2\xb9\x1e\x02\x22\x86\x5f\x5a\xf6\x7d\x01\x19\xad\x9c\x3e\xc4\xbb\x60\x12\x20\x0f\x2f\x1f\xa7\x65\x33\xc6\xbb\x50\x3c\x71\xcd\x5d\xb3\xe3\x95\x74\xe5\xbc\xb8\xed\xa7\x37\xec\x78\x9d\x2f\xcf\xb8\x2a\xc5\xfe\xde\x29\xc3\x21\xce\x7b\x3a\xbd\x70\x4f\x03\x9a\xed\xd3\xe3\xd5\x5f\x43\x32\x8d\x25\x9e\xea\x25\x80\xd0\x57\xca\x37\xfb\x8c\xc5\xf7\xcd\x66\x4e\x9b\xf0\xff\x65\xe6\xcc\x14\x71\x62\xeb\x77\xf7\xa5\x0d\x4b\xa9\xbf\xa0\x07\x75\x9a\x7d\x1f\x95\xde\x01\x8f\x25\x76\xad\xdd\x0d\x2f\xc0\x5d\x23\x41\x3d\x5f\xf7\xcf\x56\xfd\xf3\x75\x49\x8b\xe7\xe1\xb8\x6d\x92\xee\xe1\x92\x96\x99\x3c\xa7\xf0\x52\xa1\x3c\x64\xca\x84\x77\x02\x46\x2d\xaf\x71\x39\x99\xe3\x29\x70\x02\xba\x9b\x68\x64\x84\x44\x7d\xe8\x9b\xd6\xc0\x9b\x7f\xdd\x45\x2e\x3a\x8b\x39\xfa\xac\x44\x0d\x2d\x9f\xcf\xcb\xcf\x37\x6f\x7f\x06\xc5\x40\xa1\x1b\x7f\xbe\x7a\x08\xc6\x9e\x5a\x87\xcb\xe6\xc2\x43\x67\x6c\x2a\x79\x2b\x6b\x1c\x26\x1a\x3d\xba\x3a\x7c\x65\x08\x13\xaf\x78\xf8\xe1\xe1\x52\x5c\xfc\xed\xfa\xc3\x9c\x83\x33\x56\x0f\xae\xcf\x3b\x78\xfd\x10\x38\xf9\x7c\xf5\x70\x78\x93\xd2\x36\xe3\x54\x3d\x72\xaa\x6a\xf5\x64\x4d\x9a\xd9\x7a\x9d\x55\xb4\x8e\xaf\xcc\x6b\x87\x39\x3a\x34\x19\xae\x9d\x6d\x3d\xfe\xf1\xf5\xba\x40\x9f\x44\x96\x93\xc0\x72\xe9\xeb\xea\x95\x0d\xf9\x5c\xea\x72\xb2\x3e\xff\x70\xec\xb4\xa6\xcc\x59\xb6\xb9\x0f\x3e\xd1\x24\x2d\x07\xcf\x4a\x28\x5e\x1b\xf4\x8f\xd6\x7d\x5d\x6b\xc3\x6b\xf1\xf3\xd7\x1d\xe1\xe3\x5f\xc2\x5e\x92\x55\x94\x44\x7c\xaf\xd4\x53\xd2\x69\x26\xda\x70\x40\x94\x70\x69\x1f\xcf\x21\x79\x3d\x42\x92\x55\xb6\xd5\x69\x61\x6d\x51\x61\xc0\x21\xfe\x04\xde\x28\xf2\xdd\xd5\xba\xfb\x4c\x24\xc7\x8b\x85\x81\xa5\x1b\xf6\x45\x87\xde\xab\xe2\x4c\x8f\x9a\x9e\x0f\xd1\x3d\x3e\x0a\xbf\xb6\xe8\xf6\xb3\x15\x7f\x26\xea\xe1\x2c\x5c\x0e\xaf\xfc\xe1\x1b\x21\x7b\x55\x14\x64\x0a\xd5\x50\xa8\xf1\xe3\x63\x27\xc5\x77\xae\xa0\x63\xa9\x76\x15\x78\xaf\x0a\x0e\x35\xe8\x55\x91\xe4\x54\x79\x74\x7c\xf9\x0d\x25\xb7\x00\x34\xd4\xe0\xf0\xb5\x64\x52\x81\xcf\x25\xe6\xec\x65\x0e\xa0\x74\x1c\x18\x54\xf5\xe1\x05\x97\xef\x49\xb6\x77\xfd\xd7\xdf\xdd\xd5\xe1\x5f\x30\x90\x74\x1f\x72\x77\xb1\xc5\xc7\x81\x7f\x03\xde\xb5\xd8\x7d\xe3\xb4\x4e\x2e\xd2\x28\x39\xdc\x63\x2a\xcb\xb0\xf1\xa8\x6f\x8f\x3f\xd4\xae\x56\x93\xaf\xb0\xe1\xef\x68\x62\x85\xcf\x0f\x17\xd1\x2a\xea\x4f\x3d\x0e\x11\xfe\x2f\x00\x00\xff\xff\x3b\xc1\x21\x25\xa4\x16\x00\x00") +var _manifests00CustomResourceDefinitionInternalYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x59\x5f\x6f\xdc\xc6\x11\x7f\xd7\xa7\x18\x9c\x1f\xf2\xa2\xe3\x45\x71\x5b\x14\x07\x14\x85\x21\xc7\x81\x50\xc7\x35\x2c\xd5\x01\x6a\x0b\xc8\x1e\x77\xc8\x9b\x98\xdc\x65\x76\x96\xa7\x9c\x8a\x7e\xf7\x62\x76\x97\x3c\xf2\x8e\x3c\x09\x75\xb2\x4f\xba\xe1\x70\xfe\xfc\xe6\xef\x52\xaa\xa1\x8f\xe8\x98\xac\x59\x83\x6a\x08\x7f\xf3\x68\xe4\x17\x67\x5f\xfe\xca\x19\xd9\xd5\xee\x6a\x83\x5e\x5d\x5d\x7c\x21\xa3\xd7\x70\xdd\xb2\xb7\xf5\x07\x64\xdb\xba\x1c\x5f\x63\x41\x86\x3c\x59\x73\x51\xa3\x57\x5a\x79\xb5\xbe\x00\x30\xaa\xc6\x35\x68\xc3\x0e\x73\xeb\x34\x67\x64\x4a\x87\xcc\x99\x6d\xd0\x29\x6f\x9d\xfc\x61\x78\x4b\x85\xcf\xc8\x5e\x70\x83\xb9\xbc\x56\x3a\xdb\x36\x6b\x38\xcf\x1c\xa5\xb3\xf0\x03\x44\x9b\x5e\xbf\xbb\xfd\x10\x14\x05\x5a\x45\xec\xff\x31\xa6\xbf\x25\xf6\xe1\x59\x53\xb5\x4e\x55\x43\xd3\x02\x99\xc9\x94\x6d\xa5\xdc\xe0\xc1\x05\x00\xe7\xb6\xc1\x35\x2c\x16\x17\x00\x8d\x43\x46\xb7\xc3\x7f\x99\x2f\xc6\x3e\x98\x37\x84\x95\xe6\x35\x14\xaa\x62\x14\xd6\x76\xe3\x12\x24\xc9\x32\xf6\xca\xb7\xbc\x86\xff\xfc\xf7\x02\x60\xa7\x2a\xd2\x4a\x60\x8a\x0f\xc5\xa1\x57\xef\x6f\x3e\xbe\xbc\xcd\xb7\x58\xab\x48\x04\xd0\xc8\xb9\xa3\x26\xf0\xc1\xa2\xb7\x1e\x88\x41\x89\x33\x10\x4d\x83\x5a\x19\x55\xa2\x06\x32\xe0\xb7\x08\x8f\xd6\x20\x83\x96\x50\xa0\x86\xcd\x5e\x9c\xc8\x72\x6b\x0a\x2a\x47\xd0\xad\xf2\xaa\x65\x8f\x2e\x69\x03\xc8\x04\xf8\xac\x69\x37\x15\xe5\xff\xb6\x06\x41\x19\xdd\x11\x1d\xed\x94\x47\xa1\x66\xf0\xd9\xc0\x75\x7c\x15\x94\xae\xc9\x88\x01\xd4\xb4\x55\xf0\x08\x6c\x01\x7e\x4b\xdc\x4b\xed\x80\x10\xb3\x8d\xf5\xc0\x6d\xd3\x58\xe7\x51\x67\x70\xb7\x25\x1e\x3d\xb7\xa6\xda\x43\x61\x1d\x90\xf1\xe8\x8c\xaa\x20\xb7\x75\xdd\x1a\xca\x83\xec\x5e\xa6\x2d\xe0\x9f\x0d\x9a\x5b\xf1\x04\xba\xbc\xe0\x6c\x91\x18\xfc\x5e\x02\x65\x37\xbf\x60\xee\x13\xa9\x71\xc2\xe6\xa9\x8b\x87\x9c\x41\xaa\xf7\xb4\x23\xd4\xbf\x91\xb0\x44\x9e\x84\x28\x07\x8c\x77\x91\x86\x1a\x38\x84\xac\xf3\x1a\x1c\x86\xd4\x30\x7e\x6c\x70\x32\x5a\x99\x64\x55\x06\xb7\x92\x3e\x8e\x81\xb7\xb6\xad\x34\xe4\xd6\xec\xd0\xf9\x10\xd2\xd2\xd0\x63\x2f\x99\xc1\xdb\xa0\xb2\x52\x1e\xd9\x8f\x24\xf6\x28\xed\x54\xd5\xe2\x65\x88\x57\xad\xf6\xe0\x50\x74\x40\x6b\x06\xd2\x02\x0b\x67\xf0\xa3\x75\x08\x64\x0a\xbb\x86\xad\xf7\x0d\xaf\x57\xab\x92\x7c\x57\xdc\x09\x6e\xbf\x5f\xe5\xd6\x78\x47\x9b\x56\x80\x5d\x69\xdc\x61\xb5\x62\x2a\x97\xca\xe5\x5b\xf2\x98\xfb\xd6\xe1\x4a\x35\xb4\x0c\x86\x1b\x1f\x3a\x44\xad\x5f\xf4\x69\xff\xcd\xc0\xd2\x18\x0f\xf6\x8e\x4c\xd9\x93\x43\xad\xce\xe2\x2e\x15\x1b\x13\x3d\xbe\x16\xed\x3f\xc0\x2b\x24\x41\xe5\xc3\xf7\xb7\x77\x87\x14\x1a\x25\x5e\xc0\x3c\xa0\x7d\x78\x8d\x0f\xc0\x0b\x50\x64\x0a\x74\x31\x70\x85\xb3\x75\x90\x88\x46\x37\x96\x8c\x0f\x3f\xf2\x8a\xd0\x8c\x41\xe7\x76\x53\x93\x97\x48\xff\xda\x22\x7b\x89\x4f\x06\xd7\xca\x48\x6e\x6f\x10\xda\x46\xab\x90\xdc\x37\x06\xae\x55\x8d\xd5\xb5\x62\xfc\xc3\x61\x17\x84\x79\x29\x90\x3e\x0d\xfc\xb0\x33\x8f\x19\x47\x15\x03\xd0\xf5\xe1\xc9\x08\xc9\x43\x09\x90\xa0\x24\x7f\x53\x91\x8a\x34\x96\x02\x0a\x37\x39\x69\x40\xb8\x55\x3b\xb2\xae\xa7\x1b\x0e\x6d\x2b\x7b\x4a\x3d\x04\x88\x45\xc6\xd0\x88\xa5\x08\x78\xa7\x6a\x1c\xd1\x62\x27\xbc\xbb\x7b\x3b\x45\xdd\x37\x63\x66\xaf\x5c\x89\x7e\x98\x28\x53\xdd\x21\x78\x1c\x55\x8d\x89\x47\x40\x24\x9e\x0e\x8b\xad\x65\x2f\x13\xa9\x73\xf7\xd0\xa7\x8f\x84\x4c\xc6\x26\x45\x88\xcc\x5b\x34\xa5\xdf\xae\xe1\x6a\xf4\xa8\x77\xf3\xac\x45\x3d\x57\x67\x53\x9a\x13\x81\x62\x40\xe0\x37\x9a\x33\xb8\x29\xe0\x11\x9d\xbd\x4c\xe1\x2a\x54\x5b\xf9\x23\xb9\x20\x22\x5e\x7e\x9b\x4d\xda\x2e\xed\xa7\x1c\xcc\x8f\x78\x0a\xeb\x6a\xe5\xc3\xd3\xbf\xfc\xe9\xd4\x31\xaa\xdb\x7a\x0d\xdf\x4e\xb9\x25\x42\x9f\xe1\xd7\xbe\xe9\xc1\x1e\x0c\x41\x31\x29\x83\x37\xd6\x01\xfe\xa6\xea\xa6\xc2\x4b\x58\xbc\x5a\x80\x3d\x36\x0f\x60\x71\xfd\xee\xd5\x8f\xdf\x2f\xa6\x7d\x9a\x8c\x07\x9a\xb6\x3e\xb6\x6c\x09\x41\xcc\x09\xf5\xd5\x88\x92\x52\xed\xac\x5b\x89\x07\x94\xeb\x23\x95\x48\xd3\x26\x2a\xe7\xd4\xfe\x14\xd8\x1b\x8f\x35\x1f\x27\x0c\x00\x05\xf2\x09\x08\x93\xde\xa6\x25\x65\xb6\xe4\xc3\xe3\x0e\xfb\xda\x72\x18\x57\x68\x7c\xb5\x07\xbb\x09\xfb\x90\xee\x98\x0e\xc5\xfe\xe1\xb9\xd5\x3e\x57\x84\x9d\xe8\x1f\xd0\xc8\xa0\x3f\x1a\xd7\x27\x56\x9e\xb2\x3f\x61\x71\xd9\x33\x9e\xa0\x74\xa8\xe1\xe4\x05\xc0\x4f\x5b\x34\x63\xa2\x88\x4f\x5d\x3f\x96\x52\xe8\xe1\xb6\xaa\xd0\x25\x3a\x9f\xc2\x1f\xd8\x9c\x43\x6e\xac\xd1\x32\xcc\x52\xe4\xc9\x00\xaa\x7c\xdb\xef\x74\x8f\x61\xe3\x92\x52\x55\x26\x49\x0b\x0b\x92\x3a\x11\xd9\x28\xe7\x29\x97\xb5\x35\xbc\x04\x85\xa2\x8a\xc5\x20\xe5\xc3\xdf\xad\x0b\x65\x13\xf5\x1c\xb6\xc5\x14\x2f\x69\x09\x34\x89\x81\x68\xeb\xd6\x4a\x60\x1b\x05\x1e\xb9\x99\x2b\x59\x8f\x3c\xba\x9a\x0c\x46\x0e\xf2\x60\x10\xb5\x8c\xc7\x13\x91\x0e\xbd\xdb\x07\x19\x03\x8f\xc2\x5b\xdd\x24\x89\x7e\xff\x2e\x5d\x27\xac\xc3\x67\x33\x26\x2e\xcc\x52\x7e\x03\x40\x52\xe8\x8f\xc2\x32\x6f\xd6\x54\x55\xce\xd4\xde\x48\xf9\xeb\x77\xb7\xb2\x55\xdf\x8e\x6a\xeb\x60\x83\xea\x2c\x78\x20\xbf\x25\x23\xfb\x50\x82\xe8\x44\x2c\x4c\x5a\x37\x5b\x6e\xf1\xcc\x15\x5d\x3c\x7d\x56\x4c\x3e\x3d\xbe\xa1\x1c\xb8\x03\x98\xca\xec\x61\x48\x62\xb6\x39\x49\x95\x04\x5f\xc4\xcf\x49\x99\x30\xc0\xbc\xcb\xbb\x70\xe7\xb8\x29\x20\xdc\x4d\x78\xdb\x2d\x7f\x89\xb1\xcf\x74\x84\xcf\x8b\x37\x8a\x2a\xd4\x9f\x17\x33\xb2\x7b\x83\xe0\x81\xaa\x4a\x16\x36\x46\x1f\x0d\x12\xa8\x15\x5b\x13\xd7\x68\x64\x56\x25\x26\x0f\x37\x49\xe3\x9c\x50\xd5\x72\x3f\xf0\x53\xad\x65\xd3\x16\xcc\x27\x4b\x3c\x33\x29\x33\x01\x77\x4a\x9c\xeb\xde\x21\x62\xf8\xa5\x65\xdf\x25\x90\xd1\xca\xe9\x33\x75\xdd\x9d\x22\xdc\x5d\x4f\xd3\x66\x68\xef\x4c\xf2\xc4\x33\xb5\xa5\x0d\xcf\x32\xa5\xf3\xec\x63\x3f\x5e\xd0\x86\xe7\x7c\x7a\xc6\x53\x29\xf6\x77\x4e\x19\x0e\x7e\xde\xd1\xe9\xbe\x76\xea\xd0\xe4\x98\x1f\x9e\xae\x9f\x48\x7b\x5a\x7a\xaa\xe7\x0c\x84\x2e\x53\xbe\x5a\x67\x4c\xbe\xaf\x16\x73\x3a\xc3\xff\x2f\x31\x67\x96\xd0\x13\x59\x7f\xb8\x2e\x6d\x58\x52\xfd\x19\x3d\x28\x71\x76\x7d\x34\xcc\xac\x87\x2d\xa6\xd6\xee\xfa\x79\x9d\x1a\x09\xea\xe9\xbc\x7f\x32\xeb\x9f\xce\x4b\x9a\xad\x87\xe3\xb6\x49\xba\x33\x97\xb4\x5c\xe9\x0a\xc2\x34\x0d\x65\xac\xca\x95\x92\x51\x83\xb7\x50\x90\x39\xbe\x44\x8c\x8c\x4e\x0b\xb1\xdc\x40\xfa\xcd\xe1\xb3\x01\x6b\xe0\xd5\x4f\xb7\x11\x8b\x24\xb1\x40\x9f\x6f\x51\x43\xcb\xe7\xe3\xf2\xf3\xcd\xeb\x9f\x41\x31\x50\xe8\xc6\x9f\xae\xee\x83\xb0\x47\x59\x25\x66\xc5\x85\x97\xce\xc8\x54\x72\xa9\x6f\x1c\x2e\xfb\x85\x41\x87\x4f\x78\x41\xc3\x77\xf7\x97\xa2\xe2\x87\xeb\xf7\x53\x0a\xce\x48\x3d\xa8\x3e\xaf\xe0\xe5\x7d\xc0\xe4\xd3\xd5\xfd\xe1\x22\xae\x6d\xce\x99\x7a\xe0\x4c\xd5\xea\xd1\x9a\x2c\xb7\xf5\x2a\xaf\x68\x15\xbf\xb8\xac\x1c\x16\xe8\xd0\xe4\xb8\x72\xb6\xf5\xf8\xe7\x97\xab\x12\xfd\x32\xa2\xbc\x0c\x28\x6f\x7d\x5d\xbd\xb0\x21\x9e\x73\x5d\x4e\xce\xa7\xef\x8e\x95\xd6\x94\x3b\xcb\xb6\xf0\x41\x27\x9a\x65\xcb\x41\xb3\x12\x88\x57\x06\xfd\x83\x75\x5f\x56\xda\xf0\x4a\xf4\xfc\x7d\x47\xf8\xf0\xb7\xf0\x6c\x99\x57\xb4\x8c\xf6\xbd\x50\x8f\xcb\xc4\xb9\xd4\x86\x83\x45\x4b\xde\xda\x87\x73\x96\xbc\x1c\x58\x92\x57\xb6\xd5\x59\x69\x6d\x59\x61\xb0\x43\xf4\x89\x79\x03\xcf\x77\x57\xab\xb4\x91\x4a\x79\xb1\x20\x30\x37\x61\x9f\x55\xf4\x5e\x95\x67\x7a\xd4\xb8\x3e\x84\xf7\xb8\x14\x7e\x6d\x31\x6d\x90\xc7\x19\x7f\xc6\xeb\xbe\x16\x2e\xfb\x2f\x46\xe1\x13\x33\x7b\x55\x96\x64\x4a\xd5\x50\xc8\xf1\xe3\xb2\x93\xe4\x3b\x97\xd0\x31\x55\x53\x06\xde\xa9\x92\x43\x0e\x7a\x55\x2e\x0b\xaa\x3c\x3a\xbe\xfc\x8a\x94\x9b\x31\x34\xe4\x60\xff\xb1\x6d\x94\x81\x4f\x05\xe6\xec\x30\x07\x50\x3a\x2e\x0c\xaa\x7a\xff\x8c\xe1\x7b\x12\xed\x5d\xf7\xcf\x83\xdd\xd5\xe1\x57\x10\xb0\x4c\xff\x07\xd8\xc5\x16\x1f\x6f\x5f\x6b\xf0\xae\xc5\xf4\x89\xdc\x3a\x19\xa4\x91\x72\x98\x63\x2a\xcf\xb1\xf1\xa8\xdf\x1d\x7f\xe7\x5f\x2c\x46\x1f\xf1\xc3\xcf\xc1\xc6\x0a\x9f\xee\x2f\xa2\x54\xd4\x1f\x3b\x3b\x84\xf8\xbf\x00\x00\x00\xff\xff\x45\x12\x2e\xc4\xe3\x18\x00\x00") func manifests00CustomResourceDefinitionInternalYamlBytes() ([]byte, error) { return bindataRead( @@ -351,8 +351,8 @@ func manifests00CustomResourceDefinitionInternalYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "manifests/00-custom-resource-definition-internal.yaml", size: 5796, mode: os.FileMode(420), modTime: time.Unix(1, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x2c, 0x96, 0x1d, 0x70, 0x56, 0x61, 0xe2, 0x7a, 0xfb, 0x1e, 0x5a, 0xff, 0x33, 0xc7, 0xb6, 0xf8, 0xbc, 0xa8, 0x5a, 0x54, 0xb2, 0x91, 0x5b, 0xc, 0x24, 0xfc, 0xb2, 0xc8, 0xc8, 0x43, 0x30, 0x17}} + info := bindataFileInfo{name: "manifests/00-custom-resource-definition-internal.yaml", size: 6371, mode: os.FileMode(420), modTime: time.Unix(1, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x4e, 0x9c, 0xd8, 0x64, 0xf7, 0x8e, 0xd, 0x65, 0x64, 0x22, 0xde, 0x6, 0xdc, 0x85, 0xab, 0x2c, 0xc, 0x5a, 0x89, 0xec, 0xa4, 0xc9, 0x6e, 0xf, 0x2c, 0x68, 0x3e, 0x81, 0x25, 0x6b, 0x14, 0xb1}} return a, nil } diff --git a/pkg/operator/controller/dns/controller.go b/pkg/operator/controller/dns/controller.go index 13b0b75a1..2d9546429 100644 --- a/pkg/operator/controller/dns/controller.go +++ b/pkg/operator/controller/dns/controller.go @@ -3,8 +3,12 @@ package dns import ( "context" "fmt" + "reflect" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "k8s.io/client-go/tools/record" iov1 "github.com/openshift/api/operatoringress/v1" @@ -16,6 +20,7 @@ import ( "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + utilclock "k8s.io/apimachinery/pkg/util/clock" utilerrors "k8s.io/apimachinery/pkg/util/errors" configv1 "github.com/openshift/api/config/v1" @@ -122,24 +127,29 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err zones = append(zones, *dnsConfig.Spec.PublicZone) } statuses, result := r.publishRecordToZones(zones, record) - // TODO: only update if status changed - updated := record.DeepCopy() - updated.Status.Zones = statuses - if err := r.client.Status().Update(context.TODO(), updated); err != nil { - log.Error(err, "failed to update dnsrecord; will retry", "dnsrecord", record) - return reconcile.Result{RequeueAfter: 10 * time.Second}, nil + if !dnsZoneStatusSlicesEqual(statuses, record.Status.Zones) { + updated := record.DeepCopy() + updated.Status.Zones = statuses + updated.Status.ObservedGeneration = updated.Generation + if err := r.client.Status().Update(context.TODO(), updated); err != nil { + log.Error(err, "failed to update dnsrecord; will retry", "dnsrecord", record) + return reconcile.Result{RequeueAfter: 10 * time.Second}, nil + } } return result, nil } func (r *reconciler) publishRecordToZones(zones []configv1.DNSZone, record *iov1.DNSRecord) ([]iov1.DNSZoneStatus, reconcile.Result) { - // TODO: If we compare desired zones with zones from status (i.e. actual - // zones), we can filter to list to those zones which haven't already been - // successfully ensured. Then the DNS provider wouldn't need such aggressive - // internal caching. result := reconcile.Result{} var statuses []iov1.DNSZoneStatus for i := range zones { + // Only publish the record if the DNSRecord has been modified + // (which would mean the target could have changed) or its + // status does not indicate that it has already been published. + if record.Generation == record.Status.ObservedGeneration && recordIsAlreadyPublishedToZone(record, &zones[i]) { + continue + } + zone := zones[i] condition := iov1.DNSZoneCondition{ Status: string(operatorv1.ConditionUnknown), @@ -148,11 +158,13 @@ func (r *reconciler) publishRecordToZones(zones []configv1.DNSZone, record *iov1 } if err := r.dnsProvider.Ensure(record, zone); err != nil { + log.Error(err, "failed to publish DNS record to zone", "dnsrecord", record, "dnszone", zone) condition.Status = string(operatorv1.ConditionTrue) condition.Reason = "ProviderError" condition.Message = fmt.Sprintf("The DNS provider failed to ensure the record: %v", err) result.RequeueAfter = 30 * time.Second } else { + log.Info("published DNS record to zone", "dnsrecord", record, "dnszone", zone) condition.Status = string(operatorv1.ConditionFalse) condition.Reason = "ProviderSuccess" condition.Message = "The DNS provider succeeded in ensuring the record" @@ -162,7 +174,26 @@ func (r *reconciler) publishRecordToZones(zones []configv1.DNSZone, record *iov1 Conditions: []iov1.DNSZoneCondition{condition}, }) } - return statuses, result + return mergeStatuses(record.Status.Zones, statuses), result +} + +// recordIsAlreadyPublishedToZone returns a Boolean value indicating whether the +// given DNSRecord is already published to the given zone, as determined from +// the DNSRecord's status conditions. +func recordIsAlreadyPublishedToZone(record *iov1.DNSRecord, zoneToPublish *configv1.DNSZone) bool { + for _, zoneInStatus := range record.Status.Zones { + if !reflect.DeepEqual(&zoneInStatus.DNSZone, zoneToPublish) { + continue + } + + for _, condition := range zoneInStatus.Conditions { + if condition.Type == iov1.DNSRecordFailedConditionType { + return condition.Status == string(operatorv1.ConditionFalse) + } + } + } + + return false } func (r *reconciler) delete(record *iov1.DNSRecord) error { @@ -187,3 +218,77 @@ func (r *reconciler) delete(record *iov1.DNSRecord) error { } return utilerrors.NewAggregate(errs) } + +// mergeStatuses updates or extends the provided slice of statuses with the +// provided updates and returns the resulting slice. +func mergeStatuses(statuses, updates []iov1.DNSZoneStatus) []iov1.DNSZoneStatus { + var additions []iov1.DNSZoneStatus + for i, update := range updates { + add := true + for j, status := range statuses { + if cmp.Equal(status.DNSZone, update.DNSZone) { + add = false + statuses[j].Conditions = mergeConditions(status.Conditions, update.Conditions) + } + } + if add { + additions = append(additions, updates[i]) + } + } + return append(statuses, additions...) +} + +// clock is to enable unit testing +var clock utilclock.Clock = utilclock.RealClock{} + +// mergeConditions adds or updates matching conditions, and updates +// the transition time if details of a condition have changed. Returns +// the updated condition array. +func mergeConditions(conditions, updates []iov1.DNSZoneCondition) []iov1.DNSZoneCondition { + now := metav1.NewTime(clock.Now()) + var additions []iov1.DNSZoneCondition + for i, update := range updates { + add := true + for j, cond := range conditions { + if cond.Type == update.Type { + add = false + if conditionChanged(cond, update) { + conditions[j].Status = update.Status + conditions[j].Reason = update.Reason + conditions[j].Message = update.Message + conditions[j].LastTransitionTime = now + break + } + } + } + if add { + updates[i].LastTransitionTime = now + additions = append(additions, updates[i]) + } + } + conditions = append(conditions, additions...) + return conditions +} + +func conditionChanged(a, b iov1.DNSZoneCondition) bool { + return a.Status != b.Status || a.Reason != b.Reason || a.Message != b.Message +} + +// dnsZoneStatusSlicesEqual compares two DNSZoneStatus slice values. Returns +// true if the provided values should be considered equal for the purpose of +// determining whether an update is necessary, false otherwise. The comparison +// is agnostic with respect to the ordering of status conditions but not with +// respect to zones. +func dnsZoneStatusSlicesEqual(a, b []iov1.DNSZoneStatus) bool { + conditionCmpOpts := []cmp.Option{ + cmpopts.EquateEmpty(), + cmpopts.SortSlices(func(a, b iov1.DNSZoneCondition) bool { + return a.Type < b.Type + }), + } + if !cmp.Equal(a, b, conditionCmpOpts...) { + return false + } + + return true +} diff --git a/pkg/operator/controller/dns/controller_test.go b/pkg/operator/controller/dns/controller_test.go index 61f85d30e..eb4163ed2 100644 --- a/pkg/operator/controller/dns/controller_test.go +++ b/pkg/operator/controller/dns/controller_test.go @@ -1,12 +1,14 @@ package dns import ( + "testing" + "github.com/google/go-cmp/cmp" configv1 "github.com/openshift/api/config/v1" operatorv1 "github.com/openshift/api/operator/v1" iov1 "github.com/openshift/api/operatoringress/v1" "github.com/openshift/cluster-ingress-operator/pkg/dns" - "testing" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func TestPublishRecordToZones(t *testing.T) { @@ -60,3 +62,507 @@ func TestPublishRecordToZones(t *testing.T) { } } } + +// TestPublishRecordToZonesMergesStatus verifies that publishRecordToZones +// correctly merges status updates. +func TestPublishRecordToZonesMergesStatus(t *testing.T) { + var testCases = []struct { + description string + oldZoneStatuses []iov1.DNSZoneStatus + expectChange bool + }{ + { + description: "update if old value does not have the zone", + expectChange: true, + }, + { + description: "update if value does not have the condition", + oldZoneStatuses: []iov1.DNSZoneStatus{ + { + DNSZone: configv1.DNSZone{ + ID: "zone2", + }, + Conditions: []iov1.DNSZoneCondition{}, + }, + }, + expectChange: true, + }, + { + description: "update if old value has a non-matching zone", + oldZoneStatuses: []iov1.DNSZoneStatus{ + { + DNSZone: configv1.DNSZone{ + ID: "zone1", + }, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Failed", + Status: "False", + }, + }, + }, + }, + expectChange: true, + }, + { + description: "update if status condition changes", + oldZoneStatuses: []iov1.DNSZoneStatus{ + { + DNSZone: configv1.DNSZone{ + ID: "zone2", + }, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Failed", + Status: "True", + }, + }, + }, + }, + expectChange: true, + }, + { + description: "no update if status condition does not change", + oldZoneStatuses: []iov1.DNSZoneStatus{ + { + DNSZone: configv1.DNSZone{ + ID: "zone2", + }, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Failed", + Status: "False", + Reason: "ProviderSuccess", + Message: "The DNS provider succeeded in ensuring the record", + }, + }, + }, + }, + expectChange: false, + }, + } + + for _, tc := range testCases { + record := &iov1.DNSRecord{ + Status: iov1.DNSRecordStatus{Zones: tc.oldZoneStatuses}, + } + r := &reconciler{dnsProvider: &dns.FakeProvider{}} + zone := []configv1.DNSZone{{ID: "zone2"}} + oldStatuses := record.Status.DeepCopy().Zones + newStatuses, _ := r.publishRecordToZones(zone, record) + if equal := dnsZoneStatusSlicesEqual(oldStatuses, newStatuses); !equal != tc.expectChange { + t.Fatalf("%q: expected old and new status equal to be %v, got %v\nold: %#v\nnew: %#v", tc.description, tc.expectChange, equal, oldStatuses, newStatuses) + } + } +} + +func TestDnsZoneStatusSlicesEqual(t *testing.T) { + testCases := []struct { + description string + expected bool + a, b []iov1.DNSZoneStatus + }{ + { + description: "nil slices are equal", + expected: true, + }, + { + description: "nil and non-nil slices are equal", + expected: true, + a: []iov1.DNSZoneStatus{}, + }, + { + description: "empty slices are equal", + expected: true, + a: []iov1.DNSZoneStatus{}, + b: []iov1.DNSZoneStatus{}, + }, + { + description: "zone is not ignored", + expected: false, + a: []iov1.DNSZoneStatus{ + { + DNSZone: configv1.DNSZone{ + ID: "zone1", + }, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Failed", + Status: "False", + }, + }, + }, + }, + b: []iov1.DNSZoneStatus{ + { + DNSZone: configv1.DNSZone{ + ID: "zone2", + }, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Failed", + Status: "False", + }, + }, + }, + }, + }, + { + description: "condition type is not ignored", + expected: false, + a: []iov1.DNSZoneStatus{ + { + DNSZone: configv1.DNSZone{ + ID: "zone1", + }, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Failed", + Status: "True", + }, + }, + }, + }, + b: []iov1.DNSZoneStatus{ + { + DNSZone: configv1.DNSZone{ + ID: "zone1", + }, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Ready", + Status: "True", + }, + }, + }, + }, + }, + { + description: "condition status is not ignored", + expected: false, + a: []iov1.DNSZoneStatus{ + { + DNSZone: configv1.DNSZone{ + ID: "zone1", + }, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Failed", + Status: "True", + }, + }, + }, + }, + b: []iov1.DNSZoneStatus{ + { + DNSZone: configv1.DNSZone{ + ID: "zone1", + }, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Failed", + Status: "False", + }, + }, + }, + }, + }, + { + description: "condition LastTransitionTime is not ignored", + expected: false, + a: []iov1.DNSZoneStatus{ + { + DNSZone: configv1.DNSZone{ + ID: "zone1", + }, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Failed", + Status: "True", + LastTransitionTime: metav1.Unix(0, 0), + }, + }, + }, + }, + b: []iov1.DNSZoneStatus{ + { + DNSZone: configv1.DNSZone{ + ID: "zone1", + }, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Failed", + Status: "True", + LastTransitionTime: metav1.Unix(1, 0), + }, + }, + }, + }, + }, + { + description: "condition reason is not ignored", + expected: false, + a: []iov1.DNSZoneStatus{ + { + DNSZone: configv1.DNSZone{ + ID: "zone1", + }, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Failed", + Status: "True", + Reason: "foo", + }, + }, + }, + }, + b: []iov1.DNSZoneStatus{ + { + DNSZone: configv1.DNSZone{ + ID: "zone1", + }, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Failed", + Status: "True", + Reason: "bar", + }, + }, + }, + }, + }, + { + description: "condition ordering is ignored", + expected: true, + a: []iov1.DNSZoneStatus{ + { + DNSZone: configv1.DNSZone{ + ID: "zone1", + }, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Failed", + Status: "False", + }, + { + Type: "Ready", + Status: "True", + }, + }, + }, + }, + b: []iov1.DNSZoneStatus{ + { + DNSZone: configv1.DNSZone{ + ID: "zone1", + }, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Ready", + Status: "True", + }, + { + Type: "Failed", + Status: "False", + }, + }, + }, + }, + }, + { + description: "condition duplicate is not ignored", + expected: false, + a: []iov1.DNSZoneStatus{ + { + DNSZone: configv1.DNSZone{ + ID: "zone1", + }, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Failed", + Status: "False", + }, + { + Type: "Failed", + Status: "False", + }, + }, + }, + }, + b: []iov1.DNSZoneStatus{ + { + DNSZone: configv1.DNSZone{ + ID: "zone1", + }, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Failed", + Status: "False", + }, + }, + }, + }, + }, + } + + for _, tc := range testCases { + if actual := dnsZoneStatusSlicesEqual(tc.a, tc.b); actual != tc.expected { + t.Fatalf("%q: expected %v, got %v", tc.description, tc.expected, actual) + } + } +} + +func TestRecordIsAlreadyPublishedToZone(t *testing.T) { + var ( + zoneWithId = configv1.DNSZone{ID: "foo"} + zoneWithTag = configv1.DNSZone{Tags: map[string]string{"foo": "bar"}} + ) + var testCases = []struct { + description string + zone *configv1.DNSZone + zoneStatuses []iov1.DNSZoneStatus + expect bool + }{ + { + description: "status.zones is empty", + zone: &zoneWithId, + zoneStatuses: []iov1.DNSZoneStatus{}, + expect: false, + }, + { + description: "status.zones has an entry with matching id but Failed=Unknown", + zone: &zoneWithId, + zoneStatuses: []iov1.DNSZoneStatus{ + { + DNSZone: zoneWithId, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Failed", + Status: "Unknown", + }, + }, + }, + { + DNSZone: zoneWithTag, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Failed", + Status: "Unknown", + }, + }, + }, + }, + expect: false, + }, + { + description: "status.zones has an entry with matching id but Failed=True", + zone: &zoneWithId, + zoneStatuses: []iov1.DNSZoneStatus{ + { + DNSZone: zoneWithId, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Failed", + Status: "True", + }, + }, + }, + { + DNSZone: zoneWithTag, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Failed", + Status: "True", + }, + }, + }, + }, + expect: false, + }, + { + description: "status.zones has an entry with matching tag but Failed=True", + zone: &zoneWithTag, + zoneStatuses: []iov1.DNSZoneStatus{ + { + DNSZone: zoneWithId, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Failed", + Status: "True", + }, + }, + }, + { + DNSZone: zoneWithTag, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Failed", + Status: "True", + }, + }, + }, + }, + expect: false, + }, + { + description: "status.zones has an entry with matching id and Failed=False", + zone: &zoneWithId, + zoneStatuses: []iov1.DNSZoneStatus{ + { + DNSZone: zoneWithId, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Failed", + Status: "False", + }, + }, + }, + { + DNSZone: zoneWithTag, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Failed", + Status: "True", + }, + }, + }, + }, + expect: true, + }, + { + description: "status.zones has an entry with matching tag and Failed=False", + zone: &zoneWithTag, + zoneStatuses: []iov1.DNSZoneStatus{ + { + DNSZone: zoneWithId, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Failed", + Status: "True", + }, + }, + }, + { + DNSZone: zoneWithTag, + Conditions: []iov1.DNSZoneCondition{ + { + Type: "Failed", + Status: "False", + }, + }, + }, + }, + expect: true, + }, + } + + for _, tc := range testCases { + record := &iov1.DNSRecord{ + Status: iov1.DNSRecordStatus{Zones: tc.zoneStatuses}, + } + actual := recordIsAlreadyPublishedToZone(record, tc.zone) + if actual != tc.expect { + t.Errorf("%q: expected %t, got %t", tc.description, tc.expect, actual) + } + } +} diff --git a/vendor/github.com/openshift/api/config/v1/0000_10_config-operator_01_apiserver.crd.yaml b/vendor/github.com/openshift/api/config/v1/0000_10_config-operator_01_apiserver.crd.yaml index ebabc9009..398292f0d 100644 --- a/vendor/github.com/openshift/api/config/v1/0000_10_config-operator_01_apiserver.crd.yaml +++ b/vendor/github.com/openshift/api/config/v1/0000_10_config-operator_01_apiserver.crd.yaml @@ -40,6 +40,7 @@ spec: metadata: type: object spec: + description: spec holds user settable values for configuration type: object properties: additionalCORSAllowedOrigins: @@ -226,4 +227,6 @@ spec: - Modern - Custom status: + description: status holds observed values from the cluster. They may not + be overridden. type: object diff --git a/vendor/github.com/openshift/api/config/v1/0000_10_config-operator_01_infrastructure.crd.yaml b/vendor/github.com/openshift/api/config/v1/0000_10_config-operator_01_infrastructure.crd.yaml index 80e2de3b8..35e38f103 100644 --- a/vendor/github.com/openshift/api/config/v1/0000_10_config-operator_01_infrastructure.crd.yaml +++ b/vendor/github.com/openshift/api/config/v1/0000_10_config-operator_01_infrastructure.crd.yaml @@ -11,6 +11,8 @@ spec: singular: infrastructure scope: Cluster preserveUnknownFields: false + subresources: + status: {} versions: - name: v1 served: true @@ -40,11 +42,18 @@ spec: type: object properties: cloudConfig: - description: cloudConfig is a reference to a ConfigMap containing the + description: "cloudConfig is a reference to a ConfigMap containing the cloud provider configuration file. This configuration file is used to configure the Kubernetes cloud provider integration when using the built-in cloud provider integration or the external cloud controller - manager. The namespace for this config map is openshift-config. + manager. The namespace for this config map is openshift-config. \n + cloudConfig should only be consumed by the kube_cloud_config controller. + The controller is responsible for using the user configuration in + the spec for various platforms and combining that with the user provided + ConfigMap in this field to create a stitched kube cloud config. The + controller generates a ConfigMap `kube-cloud-config` in `openshift-config-managed` + namespace with the kube cloud config is stored in `cloud.conf` key. + All the clients are expected to use the generated ConfigMap only." type: object properties: key: diff --git a/vendor/github.com/openshift/api/config/v1/0000_10_config-operator_01_oauth.crd.yaml b/vendor/github.com/openshift/api/config/v1/0000_10_config-operator_01_oauth.crd.yaml index fd763d047..3bca0f60c 100644 --- a/vendor/github.com/openshift/api/config/v1/0000_10_config-operator_01_oauth.crd.yaml +++ b/vendor/github.com/openshift/api/config/v1/0000_10_config-operator_01_oauth.crd.yaml @@ -40,7 +40,7 @@ spec: metadata: type: object spec: - description: OAuthSpec contains desired cluster auth configuration + description: spec holds user settable values for configuration type: object properties: identityProviders: @@ -637,17 +637,8 @@ spec: type: object properties: accessTokenInactivityTimeoutSeconds: - description: 'accessTokenInactivityTimeoutSeconds defines the default - token inactivity timeout for tokens granted by any client. The - value represents the maximum amount of time that can occur between - consecutive uses of the token. Tokens become invalid if they are - not used within this temporal window. The user will need to acquire - a new token to regain access once a token times out. Valid values - are integer values: x < 0 Tokens time out is enabled but tokens - never timeout unless configured per client (e.g. `-1`) x = 0 Tokens - time out is disabled (default) x > 0 Tokens time out if there - is no activity for x seconds The current minimum allowed value - for X is 300 (5 minutes)' + description: 'accessTokenInactivityTimeoutSeconds - DEPRECATED: + setting this field has no effect.' type: integer format: int32 accessTokenMaxAgeSeconds: @@ -656,6 +647,6 @@ spec: type: integer format: int32 status: - description: OAuthStatus shows current known state of OAuth server in the - cluster + description: status holds observed values from the cluster. They may not + be overridden. type: object diff --git a/vendor/github.com/openshift/api/config/v1/types_apiserver.go b/vendor/github.com/openshift/api/config/v1/types_apiserver.go index b347bd80e..2fffa794b 100644 --- a/vendor/github.com/openshift/api/config/v1/types_apiserver.go +++ b/vendor/github.com/openshift/api/config/v1/types_apiserver.go @@ -14,9 +14,11 @@ import ( type APIServer struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` + // spec holds user settable values for configuration // +kubebuilder:validation:Required // +required Spec APIServerSpec `json:"spec"` + // status holds observed values from the cluster. They may not be overridden. // +optional Status APIServerStatus `json:"status"` } diff --git a/vendor/github.com/openshift/api/config/v1/types_infrastructure.go b/vendor/github.com/openshift/api/config/v1/types_infrastructure.go index ece13868b..baa5af379 100644 --- a/vendor/github.com/openshift/api/config/v1/types_infrastructure.go +++ b/vendor/github.com/openshift/api/config/v1/types_infrastructure.go @@ -5,6 +5,7 @@ import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" // +genclient // +genclient:nonNamespaced // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:subresource:status // Infrastructure holds cluster-wide information about Infrastructure. The canonical name is `cluster` type Infrastructure struct { @@ -26,6 +27,15 @@ type InfrastructureSpec struct { // This configuration file is used to configure the Kubernetes cloud provider integration // when using the built-in cloud provider integration or the external cloud controller manager. // The namespace for this config map is openshift-config. + // + // cloudConfig should only be consumed by the kube_cloud_config controller. + // The controller is responsible for using the user configuration in the spec + // for various platforms and combining that with the user provided ConfigMap in this field + // to create a stitched kube cloud config. + // The controller generates a ConfigMap `kube-cloud-config` in `openshift-config-managed` namespace + // with the kube cloud config is stored in `cloud.conf` key. + // All the clients are expected to use the generated ConfigMap only. + // // +optional CloudConfig ConfigMapFileReference `json:"cloudConfig"` diff --git a/vendor/github.com/openshift/api/config/v1/types_oauth.go b/vendor/github.com/openshift/api/config/v1/types_oauth.go index 15bc5b1c1..a3fb7ac87 100644 --- a/vendor/github.com/openshift/api/config/v1/types_oauth.go +++ b/vendor/github.com/openshift/api/config/v1/types_oauth.go @@ -14,10 +14,11 @@ import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" type OAuth struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata"` - + // spec holds user settable values for configuration // +kubebuilder:validation:Required // +required Spec OAuthSpec `json:"spec"` + // status holds observed values from the cluster. They may not be overridden. // +optional Status OAuthStatus `json:"status"` } @@ -47,17 +48,7 @@ type TokenConfig struct { // accessTokenMaxAgeSeconds defines the maximum age of access tokens AccessTokenMaxAgeSeconds int32 `json:"accessTokenMaxAgeSeconds"` - // accessTokenInactivityTimeoutSeconds defines the default token - // inactivity timeout for tokens granted by any client. - // The value represents the maximum amount of time that can occur between - // consecutive uses of the token. Tokens become invalid if they are not - // used within this temporal window. The user will need to acquire a new - // token to regain access once a token times out. - // Valid values are integer values: - // x < 0 Tokens time out is enabled but tokens never timeout unless configured per client (e.g. `-1`) - // x = 0 Tokens time out is disabled (default) - // x > 0 Tokens time out if there is no activity for x seconds - // The current minimum allowed value for X is 300 (5 minutes) + // accessTokenInactivityTimeoutSeconds - DEPRECATED: setting this field has no effect. // +optional AccessTokenInactivityTimeoutSeconds int32 `json:"accessTokenInactivityTimeoutSeconds,omitempty"` } diff --git a/vendor/github.com/openshift/api/config/v1/zz_generated.swagger_doc_generated.go b/vendor/github.com/openshift/api/config/v1/zz_generated.swagger_doc_generated.go index 6b485ab44..5e8eca32c 100644 --- a/vendor/github.com/openshift/api/config/v1/zz_generated.swagger_doc_generated.go +++ b/vendor/github.com/openshift/api/config/v1/zz_generated.swagger_doc_generated.go @@ -244,7 +244,9 @@ func (StringSourceSpec) SwaggerDoc() map[string]string { } var map_APIServer = map[string]string{ - "": "APIServer holds configuration (like serving certificates, client CA and CORS domains) shared by all API servers in the system, among them especially kube-apiserver and openshift-apiserver. The canonical name of an instance is 'cluster'.", + "": "APIServer holds configuration (like serving certificates, client CA and CORS domains) shared by all API servers in the system, among them especially kube-apiserver and openshift-apiserver. The canonical name of an instance is 'cluster'.", + "spec": "spec holds user settable values for configuration", + "status": "status holds observed values from the cluster. They may not be overridden.", } func (APIServer) SwaggerDoc() map[string]string { @@ -800,7 +802,7 @@ func (InfrastructureList) SwaggerDoc() map[string]string { var map_InfrastructureSpec = map[string]string{ "": "InfrastructureSpec contains settings that apply to the cluster infrastructure.", - "cloudConfig": "cloudConfig is a reference to a ConfigMap containing the cloud provider configuration file. This configuration file is used to configure the Kubernetes cloud provider integration when using the built-in cloud provider integration or the external cloud controller manager. The namespace for this config map is openshift-config.", + "cloudConfig": "cloudConfig is a reference to a ConfigMap containing the cloud provider configuration file. This configuration file is used to configure the Kubernetes cloud provider integration when using the built-in cloud provider integration or the external cloud controller manager. The namespace for this config map is openshift-config.\n\ncloudConfig should only be consumed by the kube_cloud_config controller. The controller is responsible for using the user configuration in the spec for various platforms and combining that with the user provided ConfigMap in this field to create a stitched kube cloud config. The controller generates a ConfigMap `kube-cloud-config` in `openshift-config-managed` namespace with the kube cloud config is stored in `cloud.conf` key. All the clients are expected to use the generated ConfigMap only.", "platformSpec": "platformSpec holds desired information specific to the underlying infrastructure provider.", } @@ -1114,7 +1116,9 @@ func (LDAPIdentityProvider) SwaggerDoc() map[string]string { } var map_OAuth = map[string]string{ - "": "OAuth holds cluster-wide information about OAuth. The canonical name is `cluster`. It is used to configure the integrated OAuth server. This configuration is only honored when the top level Authentication config has type set to IntegratedOAuth.", + "": "OAuth holds cluster-wide information about OAuth. The canonical name is `cluster`. It is used to configure the integrated OAuth server. This configuration is only honored when the top level Authentication config has type set to IntegratedOAuth.", + "spec": "spec holds user settable values for configuration", + "status": "status holds observed values from the cluster. They may not be overridden.", } func (OAuth) SwaggerDoc() map[string]string { @@ -1208,7 +1212,7 @@ func (RequestHeaderIdentityProvider) SwaggerDoc() map[string]string { var map_TokenConfig = map[string]string{ "": "TokenConfig holds the necessary configuration options for authorization and access tokens", "accessTokenMaxAgeSeconds": "accessTokenMaxAgeSeconds defines the maximum age of access tokens", - "accessTokenInactivityTimeoutSeconds": "accessTokenInactivityTimeoutSeconds defines the default token inactivity timeout for tokens granted by any client. The value represents the maximum amount of time that can occur between consecutive uses of the token. Tokens become invalid if they are not used within this temporal window. The user will need to acquire a new token to regain access once a token times out. Valid values are integer values:\n x < 0 Tokens time out is enabled but tokens never timeout unless configured per client (e.g. `-1`)\n x = 0 Tokens time out is disabled (default)\n x > 0 Tokens time out if there is no activity for x seconds\nThe current minimum allowed value for X is 300 (5 minutes)", + "accessTokenInactivityTimeoutSeconds": "accessTokenInactivityTimeoutSeconds - DEPRECATED: setting this field has no effect.", } func (TokenConfig) SwaggerDoc() map[string]string { diff --git a/vendor/github.com/openshift/api/operatoringress/v1/0000_50_dns-record.yaml b/vendor/github.com/openshift/api/operatoringress/v1/0000_50_dns-record.yaml index 81ddf111c..59a304757 100644 --- a/vendor/github.com/openshift/api/operatoringress/v1/0000_50_dns-record.yaml +++ b/vendor/github.com/openshift/api/operatoringress/v1/0000_50_dns-record.yaml @@ -69,6 +69,15 @@ spec: description: status is the most recently observed status of the dnsRecord. type: object properties: + observedGeneration: + description: observedGeneration is the most recently observed generation + of the DNSRecord. When the DNSRecord is updated, the controller updates + the corresponding record in each managed zone. If an update for a + particular zone fails, that failure is recorded in the status condition + for the zone so that the controller can determine that it needs to + retry the update for that specific zone. + type: integer + format: int64 zones: description: zones are the status of the record in each zone. type: array diff --git a/vendor/github.com/openshift/api/operatoringress/v1/types.go b/vendor/github.com/openshift/api/operatoringress/v1/types.go index d44335d25..ae24e3b7b 100644 --- a/vendor/github.com/openshift/api/operatoringress/v1/types.go +++ b/vendor/github.com/openshift/api/operatoringress/v1/types.go @@ -56,6 +56,15 @@ type DNSRecordSpec struct { type DNSRecordStatus struct { // zones are the status of the record in each zone. Zones []DNSZoneStatus `json:"zones,omitempty"` + + // observedGeneration is the most recently observed generation of the + // DNSRecord. When the DNSRecord is updated, the controller updates the + // corresponding record in each managed zone. If an update for a + // particular zone fails, that failure is recorded in the status + // condition for the zone so that the controller can determine that it + // needs to retry the update for that specific zone. + // +optional + ObservedGeneration int64 `json:"observedGeneration,omitempty"` } // DNSZoneStatus is the status of a record within a specific zone. diff --git a/vendor/github.com/openshift/api/operatoringress/v1/zz_generated.swagger_doc_generated.go b/vendor/github.com/openshift/api/operatoringress/v1/zz_generated.swagger_doc_generated.go index 2679eaa60..b6e8a5771 100644 --- a/vendor/github.com/openshift/api/operatoringress/v1/zz_generated.swagger_doc_generated.go +++ b/vendor/github.com/openshift/api/operatoringress/v1/zz_generated.swagger_doc_generated.go @@ -42,8 +42,9 @@ func (DNSRecordSpec) SwaggerDoc() map[string]string { } var map_DNSRecordStatus = map[string]string{ - "": "DNSRecordStatus is the most recently observed status of each record.", - "zones": "zones are the status of the record in each zone.", + "": "DNSRecordStatus is the most recently observed status of each record.", + "zones": "zones are the status of the record in each zone.", + "observedGeneration": "observedGeneration is the most recently observed generation of the DNSRecord. When the DNSRecord is updated, the controller updates the corresponding record in each managed zone. If an update for a particular zone fails, that failure is recorded in the status condition for the zone so that the controller can determine that it needs to retry the update for that specific zone.", } func (DNSRecordStatus) SwaggerDoc() map[string]string { diff --git a/vendor/k8s.io/apimachinery/pkg/util/json/json.go b/vendor/k8s.io/apimachinery/pkg/util/json/json.go index 0e2e30175..204834883 100644 --- a/vendor/k8s.io/apimachinery/pkg/util/json/json.go +++ b/vendor/k8s.io/apimachinery/pkg/util/json/json.go @@ -66,11 +66,36 @@ func Unmarshal(data []byte, v interface{}) error { // If the decode succeeds, post-process the map to convert json.Number objects to int64 or float64 return convertSliceNumbers(*v, 0) + case *interface{}: + // Build a decoder from the given data + decoder := json.NewDecoder(bytes.NewBuffer(data)) + // Preserve numbers, rather than casting to float64 automatically + decoder.UseNumber() + // Run the decode + if err := decoder.Decode(v); err != nil { + return err + } + // If the decode succeeds, post-process the map to convert json.Number objects to int64 or float64 + return convertInterfaceNumbers(v, 0) + default: return json.Unmarshal(data, v) } } +func convertInterfaceNumbers(v *interface{}, depth int) error { + var err error + switch v2 := (*v).(type) { + case json.Number: + *v, err = convertNumber(v2) + case map[string]interface{}: + err = convertMapNumbers(v2, depth+1) + case []interface{}: + err = convertSliceNumbers(v2, depth+1) + } + return err +} + // convertMapNumbers traverses the map, converting any json.Number values to int64 or float64. // values which are map[string]interface{} or []interface{} are recursively visited func convertMapNumbers(m map[string]interface{}, depth int) error { diff --git a/vendor/k8s.io/apimachinery/pkg/util/net/http.go b/vendor/k8s.io/apimachinery/pkg/util/net/http.go index 0ba586bfe..7449cbb0a 100644 --- a/vendor/k8s.io/apimachinery/pkg/util/net/http.go +++ b/vendor/k8s.io/apimachinery/pkg/util/net/http.go @@ -55,6 +55,12 @@ func JoinPreservingTrailingSlash(elem ...string) string { return result } +// IsTimeout returns true if the given error is a network timeout error +func IsTimeout(err error) bool { + neterr, ok := err.(net.Error) + return ok && neterr != nil && neterr.Timeout() +} + // IsProbableEOF returns true if the given error resembles a connection termination // scenario that would justify assuming that the watch is empty. // These errors are what the Go http stack returns back to us which are general diff --git a/vendor/k8s.io/apimachinery/pkg/util/wait/wait.go b/vendor/k8s.io/apimachinery/pkg/util/wait/wait.go index 4cb0c122c..d759d912b 100644 --- a/vendor/k8s.io/apimachinery/pkg/util/wait/wait.go +++ b/vendor/k8s.io/apimachinery/pkg/util/wait/wait.go @@ -286,8 +286,9 @@ func contextForChannel(parentCh <-chan struct{}) (context.Context, context.Cance } // BackoffManager manages backoff with a particular scheme based on its underlying implementation. It provides -// an interface to return a timer for backoff, and caller shall backoff until Timer.C returns. If the second Backoff() -// is called before the timer from the first Backoff() call finishes, the first timer will NOT be drained. +// an interface to return a timer for backoff, and caller shall backoff until Timer.C() drains. If the second Backoff() +// is called before the timer from the first Backoff() call finishes, the first timer will NOT be drained and result in +// undetermined behavior. // The BackoffManager is supposed to be called in a single-threaded environment. type BackoffManager interface { Backoff() clock.Timer @@ -317,7 +318,7 @@ func NewExponentialBackoffManager(initBackoff, maxBackoff, resetDuration time.Du Steps: math.MaxInt32, Cap: maxBackoff, }, - backoffTimer: c.NewTimer(0), + backoffTimer: nil, initialBackoff: initBackoff, lastBackoffStart: c.Now(), backoffResetDuration: resetDuration, @@ -334,9 +335,14 @@ func (b *exponentialBackoffManagerImpl) getNextBackoff() time.Duration { return b.backoff.Step() } -// Backoff implements BackoffManager.Backoff, it returns a timer so caller can block on the timer for backoff. +// Backoff implements BackoffManager.Backoff, it returns a timer so caller can block on the timer for exponential backoff. +// The returned timer must be drained before calling Backoff() the second time func (b *exponentialBackoffManagerImpl) Backoff() clock.Timer { - b.backoffTimer.Reset(b.getNextBackoff()) + if b.backoffTimer == nil { + b.backoffTimer = b.clock.NewTimer(b.getNextBackoff()) + } else { + b.backoffTimer.Reset(b.getNextBackoff()) + } return b.backoffTimer } @@ -354,7 +360,7 @@ func NewJitteredBackoffManager(duration time.Duration, jitter float64, c clock.C clock: c, duration: duration, jitter: jitter, - backoffTimer: c.NewTimer(0), + backoffTimer: nil, } } @@ -366,8 +372,15 @@ func (j *jitteredBackoffManagerImpl) getNextBackoff() time.Duration { return jitteredPeriod } +// Backoff implements BackoffManager.Backoff, it returns a timer so caller can block on the timer for jittered backoff. +// The returned timer must be drained before calling Backoff() the second time func (j *jitteredBackoffManagerImpl) Backoff() clock.Timer { - j.backoffTimer.Reset(j.getNextBackoff()) + backoff := j.getNextBackoff() + if j.backoffTimer == nil { + j.backoffTimer = j.clock.NewTimer(backoff) + } else { + j.backoffTimer.Reset(backoff) + } return j.backoffTimer } diff --git a/vendor/k8s.io/apimachinery/pkg/watch/streamwatcher.go b/vendor/k8s.io/apimachinery/pkg/watch/streamwatcher.go index 8af256eb1..4269a836a 100644 --- a/vendor/k8s.io/apimachinery/pkg/watch/streamwatcher.go +++ b/vendor/k8s.io/apimachinery/pkg/watch/streamwatcher.go @@ -113,7 +113,7 @@ func (sw *StreamWatcher) receive() { case io.ErrUnexpectedEOF: klog.V(1).Infof("Unexpected EOF during watch stream event decoding: %v", err) default: - if net.IsProbableEOF(err) { + if net.IsProbableEOF(err) || net.IsTimeout(err) { klog.V(5).Infof("Unable to decode an event from the watch stream: %v", err) } else { sw.result <- Event{ diff --git a/vendor/modules.txt b/vendor/modules.txt index 547cce36b..3966d63bf 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -123,7 +123,7 @@ github.com/matttproud/golang_protobuf_extensions/pbutil github.com/modern-go/concurrent # github.com/modern-go/reflect2 v1.0.1 github.com/modern-go/reflect2 -# github.com/openshift/api v0.0.0-20200416222006-8cfd79130f9c +# github.com/openshift/api v0.0.0-20200522173408-17ada6e4245b github.com/openshift/api/config/v1 github.com/openshift/api/operator/v1 github.com/openshift/api/operatoringress/v1 @@ -282,7 +282,7 @@ gopkg.in/inf.v0 gopkg.in/yaml.v2 # gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966 gopkg.in/yaml.v3 -# k8s.io/api v0.18.0 +# k8s.io/api v0.18.3 k8s.io/api/admission/v1beta1 k8s.io/api/admissionregistration/v1 k8s.io/api/admissionregistration/v1beta1 @@ -328,7 +328,7 @@ k8s.io/api/storage/v1beta1 k8s.io/apiextensions-apiserver/pkg/apis/apiextensions k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1 k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1 -# k8s.io/apimachinery v0.18.0 +# k8s.io/apimachinery v0.18.3 k8s.io/apimachinery/pkg/api/equality k8s.io/apimachinery/pkg/api/errors k8s.io/apimachinery/pkg/api/meta @@ -462,7 +462,7 @@ k8s.io/client-go/util/keyutil k8s.io/client-go/util/workqueue # k8s.io/klog v1.0.0 k8s.io/klog -# k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c +# k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6 k8s.io/kube-openapi/pkg/util/proto # k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 k8s.io/utils/buffer