diff --git a/config/300-domain.yaml b/config/300-domain.yaml new file mode 100644 index 000000000..fa27f716b --- /dev/null +++ b/config/300-domain.yaml @@ -0,0 +1,39 @@ +# Copyright 2020 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: domains.networking.internal.knative.dev + labels: + serving.knative.dev/release: devel + knative.dev/crd-install: "true" +spec: + group: networking.internal.knative.dev + versions: + - name: v1alpha1 + served: true + storage: true + names: + kind: Domain + plural: domains + singular: domain + categories: + - knative-internal + - networking + shortNames: + - dom + scope: Cluster + subresources: + status: {} diff --git a/config/300-realm.yaml b/config/300-realm.yaml new file mode 100644 index 000000000..0ce6cd90e --- /dev/null +++ b/config/300-realm.yaml @@ -0,0 +1,44 @@ +# Copyright 2020 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: realms.networking.internal.knative.dev + labels: + serving.knative.dev/release: devel + knative.dev/crd-install: "true" +spec: + group: networking.internal.knative.dev + versions: + - name: v1alpha1 + served: true + storage: true + names: + kind: Realm + plural: realms + singular: realm + categories: + - knative-internal + - networking + scope: Cluster + subresources: + status: {} + additionalPrinterColumns: + - name: Ready + type: string + JSONPath: ".status.conditions[?(@.type=='Ready')].status" + - name: Reason + type: string + JSONPath: ".status.conditions[?(@.type=='Ready')].reason" diff --git a/pkg/apis/networking/v1alpha1/domain_defaults.go b/pkg/apis/networking/v1alpha1/domain_defaults.go new file mode 100644 index 000000000..f70a6bf01 --- /dev/null +++ b/pkg/apis/networking/v1alpha1/domain_defaults.go @@ -0,0 +1,32 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "context" + + "knative.dev/pkg/apis" +) + +// SetDefaults populates default values in Domain +func (d *Domain) SetDefaults(ctx context.Context) { + d.Spec.SetDefaults(apis.WithinSpec(ctx)) +} + +// SetDefaults populates default values in DomainSpec +func (s *DomainSpec) SetDefaults(ctx context.Context) { +} diff --git a/pkg/apis/networking/v1alpha1/domain_defaults_test.go b/pkg/apis/networking/v1alpha1/domain_defaults_test.go new file mode 100644 index 000000000..e86b70206 --- /dev/null +++ b/pkg/apis/networking/v1alpha1/domain_defaults_test.go @@ -0,0 +1,33 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestDomainDefaults(t *testing.T) { + d := Domain{} + d.SetDefaults(context.Background()) + + if !cmp.Equal(Domain{}, d) { + t.Errorf("SetDefaults (-want, +got) = \n%s", cmp.Diff(Domain{}, d)) + } +} diff --git a/pkg/apis/networking/v1alpha1/domain_lifecycle.go b/pkg/apis/networking/v1alpha1/domain_lifecycle.go new file mode 100644 index 000000000..95d16c141 --- /dev/null +++ b/pkg/apis/networking/v1alpha1/domain_lifecycle.go @@ -0,0 +1,34 @@ +/* +Copyright 2020 The Knative Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "knative.dev/pkg/apis" +) + +var domainCondSet = apis.NewLivingConditionSet() + +// GetConditionSet retrieves the condition set for this resource. Implements the KRShaped interface. +func (*Domain) GetConditionSet() apis.ConditionSet { + return domainCondSet +} + +// GetGroupVersionKind returns SchemeGroupVersion of an Domain +func (*Domain) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind("Domain") +} diff --git a/pkg/apis/networking/v1alpha1/domain_lifecycle_test.go b/pkg/apis/networking/v1alpha1/domain_lifecycle_test.go new file mode 100644 index 000000000..329bfe289 --- /dev/null +++ b/pkg/apis/networking/v1alpha1/domain_lifecycle_test.go @@ -0,0 +1,39 @@ +/* +Copyright 2020 The Knative Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package v1alpha1 + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "knative.dev/pkg/apis" +) + +func TestDomainGetConditionSet(t *testing.T) { + d := Domain{} + + if got, want := d.GetConditionSet().GetTopLevelConditionType(), apis.ConditionReady; got != want { + t.Errorf("GetConditionSet=%v, want=%v", got, want) + } +} + +func TestDomainGetGroupVersionKind(t *testing.T) { + d := Domain{} + expected := SchemeGroupVersion.WithKind("Domain") + if !cmp.Equal(expected, d.GetGroupVersionKind()) { + t.Errorf("Unexpected diff (-want, +got) = %v", cmp.Diff(expected, d.GetGroupVersionKind())) + } +} diff --git a/pkg/apis/networking/v1alpha1/domain_types.go b/pkg/apis/networking/v1alpha1/domain_types.go new file mode 100644 index 000000000..2c392c628 --- /dev/null +++ b/pkg/apis/networking/v1alpha1/domain_types.go @@ -0,0 +1,160 @@ +/* +Copyright 2020 The Knative Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" + duckv1 "knative.dev/pkg/apis/duck/v1" + "knative.dev/pkg/kmeta" +) + +// +genclient +// +genclient:nonNamespaced +// +genreconciler:krshapedlogic=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Domain is a cluster-scoped resource to configure a proxy pool for a given Route. +type Domain struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec is the desired state of the Domain. + // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status + // +optional + Spec DomainSpec `json:"spec,omitempty"` + + // Status is the current state of the Domain. + // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status + // +optional + Status DomainStatus `json:"status,omitempty"` +} + +// Verify that Domain adheres to the appropriate interfaces. +var ( + // Check that Doimain may be validated and defaulted. + _ apis.Validatable = (*Domain)(nil) + _ apis.Defaultable = (*Domain)(nil) + + // Check that we can create OwnerReferences to a Domain. + _ kmeta.OwnerRefable = (*Domain)(nil) + + // Check that the type conforms to the duck Knative Resource shape. + _ duckv1.KRShaped = (*Domain)(nil) +) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// DomainList is a collection of Domain objects. +type DomainList struct { + metav1.TypeMeta `json:",inline"` + // Standard object metadata. + // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + + // Items is the list of Domain objects. + Items []Domain `json:"items"` +} + +// DomainSpec describes the Ingress the user wishes to exist. +// +// In general this follows the same shape as K8s Ingress. +// Some notable differences: +// - Backends now can have namespace: +// - Traffic can be split across multiple backends. +// - Timeout & Retry can be configured. +// - Headers can be appended. +// DomainSpec contains the specification of the Domain CRD. +type DomainSpec struct { + // IngressClass tells what Ingress class annotation to use for Routes associated + // with this Realm. + IngressClass string `json:"ingressClass"` + + // Suffix specifies the domain suffix to be used. This field replaces the + // existing config-domain ConfigMap. Internal Domains can omit this, in + // which case we will default to the cluster suffix. + // +optional + Suffix string `json:"suffix,omitempty"` + + // LoadBalancers provide addresses (IP addresses, domains) of the load balancers + // associated with this Domain. This is used in automatic DNS provisioning like + // configuration of magic DNS or creating ExternalName services for cluster-local + // access. + LoadBalancers []LoadBalancerIngressSpec `json:"loadBalancers"` + + // Configs contains additional pieces of information to configure ingress proxies. + // +optional + Configs []IngressConfig `json:"configs,omitempty"` +} + +// IngressConfig allows KIngress implementations to add additional information needed +// for configuring the proxies associated with this Domain. +// For examples, in our Istio-based Ingress this will contains all references of +// Istio Gateways associated with this Domain. This could be a reference of a ConfigMap +// owned by the implementation as well. +type IngressConfig struct { + // Name of the Kingress implementation resource + // +optional + Name string `json:"name,omitempty"` + // Namespace of the Kingress implementation resource + // +optional + Namespace string `json:"namespace,omitempty"` + // Type of the Kingress implementation resource + // +optional + Type string `json:"type,omitempty"` +} + +// LoadBalancerIngressSpec represents the spec of a load-balancer ingress point: +// traffic intended for the service should be sent to an ingress point. +type LoadBalancerIngressSpec struct { + // IP is set for load-balancer ingress points that are IP based + // (typically GCE or OpenStack load-balancers) + // +optional + IP string `json:"ip,omitempty"` + + // Domain is set for load-balancer ingress points that are DNS based + // (typically AWS load-balancers) + // +optional + Domain string `json:"domain,omitempty"` + + // DomainInternal is set if there is a cluster-local DNS name to access the Ingress. + // + // NOTE: This differs from K8s Ingress, since we also desire to have a cluster-local + // DNS name to allow routing in case of not having a mesh. + // + // +optional + DomainInternal string `json:"domainInternal,omitempty"` + + // MeshOnly is set if the Ingress is only load-balanced through a Service mesh. + // +optional + MeshOnly bool `json:"meshOnly,omitempty"` +} + +// DomainStatus will reflect Ready=True if the implementation accepts the Domain data +// as valid. +type DomainStatus struct { + duckv1.Status `json:",inline"` +} + +// GetStatus retrieves the status of the Domain. Implements the KRShaped interface. +func (d *Domain) GetStatus() *duckv1.Status { + return &d.Status.Status +} diff --git a/pkg/apis/networking/v1alpha1/domain_types_test.go b/pkg/apis/networking/v1alpha1/domain_types_test.go new file mode 100644 index 000000000..d146b2f91 --- /dev/null +++ b/pkg/apis/networking/v1alpha1/domain_types_test.go @@ -0,0 +1,28 @@ +/* +Copyright 2020 The Knative Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package v1alpha1 + +import "testing" + +func TestDomainGetStatus(t *testing.T) { + r := &Domain{ + Status: DomainStatus{}, + } + + if got, want := r.GetStatus(), &r.Status.Status; got != want { + t.Errorf("GetStatus=%v, want=%v", got, want) + } +} diff --git a/pkg/apis/networking/v1alpha1/domain_validation.go b/pkg/apis/networking/v1alpha1/domain_validation.go new file mode 100644 index 000000000..d03ccb1d2 --- /dev/null +++ b/pkg/apis/networking/v1alpha1/domain_validation.go @@ -0,0 +1,70 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "context" + + "k8s.io/apimachinery/pkg/api/equality" + "knative.dev/pkg/apis" +) + +// Validate inspects and validates Domain object. +func (d *Domain) Validate(ctx context.Context) *apis.FieldError { + return d.Spec.Validate(apis.WithinSpec(ctx)).ViaField("spec") +} + +// Validate inspects and validates DomainSpec object. +func (spec *DomainSpec) Validate(ctx context.Context) *apis.FieldError { + // Spec must not be empty. + if equality.Semantic.DeepEqual(spec, &DomainSpec{}) { + return apis.ErrMissingField(apis.CurrentField) + } + var all *apis.FieldError + if spec.IngressClass == "" { + all = all.Also(apis.ErrMissingField("ingressClass")) + } + if len(spec.LoadBalancers) == 0 { + all = all.Also(apis.ErrMissingField("loadBalancers")) + } + for idx, lbSpec := range spec.LoadBalancers { + all = all.Also(lbSpec.Validate(ctx).ViaFieldIndex("loadBalancers", idx)) + } + for idx, cfg := range spec.Configs { + all = all.Also(cfg.Validate(ctx).ViaFieldIndex("configs", idx)) + } + return all +} + +func (lb *LoadBalancerIngressSpec) Validate(ctx context.Context) *apis.FieldError { + var all *apis.FieldError + if lb.Domain == "" && lb.DomainInternal == "" && lb.IP == "" && !lb.MeshOnly { + return all.Also(apis.ErrMissingOneOf("domain", "domainInternal", "ip", "meshOnly")) + } + return all +} + +func (cfg *IngressConfig) Validate(ctx context.Context) *apis.FieldError { + var all *apis.FieldError + if cfg.Name == "" { + all = all.Also(apis.ErrMissingField("name")) + } + if cfg.Type == "" { + all = all.Also(apis.ErrMissingField("type")) + } + return all +} diff --git a/pkg/apis/networking/v1alpha1/domain_validation_test.go b/pkg/apis/networking/v1alpha1/domain_validation_test.go new file mode 100644 index 000000000..34fd71ef2 --- /dev/null +++ b/pkg/apis/networking/v1alpha1/domain_validation_test.go @@ -0,0 +1,108 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" +) + +func TestDomainSpecValidation(t *testing.T) { + tests := []struct { + name string + ds DomainSpec + want *apis.FieldError + }{{ + name: "all good", + ds: DomainSpec{ + IngressClass: "test-ingress-class", + LoadBalancers: []LoadBalancerIngressSpec{{ + Domain: "test-domain", + }}, + }, + }, { + name: "no spec", + ds: DomainSpec{}, + want: apis.ErrMissingField("spec"), + }, { + name: "ingress class isnt specified", + ds: DomainSpec{ + LoadBalancers: []LoadBalancerIngressSpec{{ + Domain: "test-domain", + }}, + }, + want: apis.ErrMissingField("spec.ingressClass"), + }, { + name: "loadbalacers arent specified", + ds: DomainSpec{ + IngressClass: "test-ingress-class", + }, + want: apis.ErrMissingField("spec.loadBalancers"), + }, { + name: "at least one field in loadbalacer is specified", + ds: DomainSpec{ + IngressClass: "test-ingress-class", + LoadBalancers: []LoadBalancerIngressSpec{{ + Domain: "some-domain", + }}, + }, + }, { + name: "none of the fields are specified in a loadbalancer", + ds: DomainSpec{ + IngressClass: "test-ingress-class", + LoadBalancers: []LoadBalancerIngressSpec{{}}, + }, + want: apis.ErrMissingOneOf("spec.loadBalancers[0].domain", "spec.loadBalancers[0].domainInternal", + "spec.loadBalancers[0].ip", "spec.loadBalancers[0].meshOnly"), + }, { + name: "name is missing from ingressConfig", + ds: DomainSpec{ + IngressClass: "test-ingress-class", + Configs: []IngressConfig{{Namespace: "ns", Type: "my-type"}}, + LoadBalancers: []LoadBalancerIngressSpec{{ + Domain: "some-domain", + }}, + }, + want: apis.ErrMissingField("spec.configs[0].name"), + }, { + name: "type is missing from ingressConfig", + ds: DomainSpec{ + IngressClass: "test-ingress-class", + Configs: []IngressConfig{{Namespace: "ns", Name: "my-name"}}, + LoadBalancers: []LoadBalancerIngressSpec{{ + Domain: "some-domain", + }}, + }, + want: apis.ErrMissingField("spec.configs[0].type"), + }} + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + ds := Domain{ + ObjectMeta: metav1.ObjectMeta{Name: "test-ds"}, + Spec: test.ds, + } + got := ds.Validate(context.Background()) + if !cmp.Equal(test.want.Error(), got.Error()) { + t.Errorf("Validate (-want, +got) = \n%s", cmp.Diff(test.want.Error(), got.Error())) + } + }) + } +} diff --git a/pkg/apis/networking/v1alpha1/realm_defaults.go b/pkg/apis/networking/v1alpha1/realm_defaults.go new file mode 100644 index 000000000..c84ff0aa6 --- /dev/null +++ b/pkg/apis/networking/v1alpha1/realm_defaults.go @@ -0,0 +1,32 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "context" + + "knative.dev/pkg/apis" +) + +// SetDefaults populates default values in Realm +func (r *Realm) SetDefaults(ctx context.Context) { + r.Spec.SetDefaults(apis.WithinSpec(ctx)) +} + +// SetDefaults populates default values in RealmSpec +func (s *RealmSpec) SetDefaults(ctx context.Context) { +} diff --git a/pkg/apis/networking/v1alpha1/realm_defaults_test.go b/pkg/apis/networking/v1alpha1/realm_defaults_test.go new file mode 100644 index 000000000..ce2cee698 --- /dev/null +++ b/pkg/apis/networking/v1alpha1/realm_defaults_test.go @@ -0,0 +1,33 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestRealmDefaults(t *testing.T) { + r := Realm{} + r.SetDefaults(context.Background()) + + if !cmp.Equal(Realm{}, r) { + t.Errorf("SetDefaults (-want, +got) = \n%s", cmp.Diff(Realm{}, r)) + } +} diff --git a/pkg/apis/networking/v1alpha1/realm_lifecycle.go b/pkg/apis/networking/v1alpha1/realm_lifecycle.go new file mode 100644 index 000000000..61d9cac68 --- /dev/null +++ b/pkg/apis/networking/v1alpha1/realm_lifecycle.go @@ -0,0 +1,34 @@ +/* +Copyright 2020 The Knative Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "knative.dev/pkg/apis" +) + +var realmCondSet = apis.NewLivingConditionSet() + +// GetConditionSet retrieves the condition set for this resource. Implements the KRShaped interface. +func (*Realm) GetConditionSet() apis.ConditionSet { + return realmCondSet +} + +// GetGroupVersionKind returns SchemeGroupVersion of an Realm +func (i *Realm) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind("Realm") +} diff --git a/pkg/apis/networking/v1alpha1/realm_lifecycle_test.go b/pkg/apis/networking/v1alpha1/realm_lifecycle_test.go new file mode 100644 index 000000000..c4866d490 --- /dev/null +++ b/pkg/apis/networking/v1alpha1/realm_lifecycle_test.go @@ -0,0 +1,39 @@ +/* +Copyright 2020 The Knative Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package v1alpha1 + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "knative.dev/pkg/apis" +) + +func TestRealmGetConditionSet(t *testing.T) { + r := Realm{} + + if got, want := r.GetConditionSet().GetTopLevelConditionType(), apis.ConditionReady; got != want { + t.Errorf("GetConditionSet=%v, want=%v", got, want) + } +} + +func TestRealmGetGroupVersionKind(t *testing.T) { + r := Realm{} + expected := SchemeGroupVersion.WithKind("Realm") + if !cmp.Equal(expected, r.GetGroupVersionKind()) { + t.Errorf("Unexpected diff (-want, +got) = %v", cmp.Diff(expected, r.GetGroupVersionKind())) + } +} diff --git a/pkg/apis/networking/v1alpha1/realm_types.go b/pkg/apis/networking/v1alpha1/realm_types.go new file mode 100644 index 000000000..6366f48d4 --- /dev/null +++ b/pkg/apis/networking/v1alpha1/realm_types.go @@ -0,0 +1,108 @@ +/* +Copyright 2020 The Knative Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" + duckv1 "knative.dev/pkg/apis/duck/v1" + "knative.dev/pkg/kmeta" +) + +// +genclient +// +genclient:nonNamespaced +// +genreconciler:krshapedlogic=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Realm is a cluster-scoped resource that specifies a Route visibility. +type Realm struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec is the desired state of the Realm. + // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status + // +optional + Spec RealmSpec `json:"spec,omitempty"` + + // Status is the current state of the Realm. + // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status + // +optional + Status RealmStatus `json:"status,omitempty"` +} + +// Verify that Realm adheres to the appropriate interfaces. +var ( + // Check that Realm may be validated and defaulted. + _ apis.Validatable = (*Realm)(nil) + _ apis.Defaultable = (*Realm)(nil) + + // Check that we can create OwnerReferences to a Realm. + _ kmeta.OwnerRefable = (*Realm)(nil) + + // Check that the type conforms to the duck Knative Resource shape. + _ duckv1.KRShaped = (*Realm)(nil) +) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// RealmList is a collection of Realm objects. +type RealmList struct { + metav1.TypeMeta `json:",inline"` + // Standard object metadata. + // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + + // Items is the list of Realm objects. + Items []Realm `json:"items"` +} + +// RealmSpec defines the specifications of a Realm. +// Realms specify an internal and external Domain, but disallow arbitrary combinations. +// Operators can create combinations of Domains that makes sense for their clusters, +// and developers can only switch between these predefined Realms. +// +// Conditions: Currently each Realm can have only two Domains. +// That fits well with the way we assign Route.Status.URL and Route.Status.Domain. +// All Domains in the same Realm must share the same ingress class annotation. +type RealmSpec struct { + // External contains the name of the Domain resource corresponding with traffic + // originating from outside of the cluster. Could be omitted for a Realm without + // external access. + // +optional + External string + + // Cluster contains the name of the Domain resource corresponding with traffic + // originating from inside of the cluster. Could be omitted for a Realm without + // internal access. + // +optional + Cluster string +} + +// RealmStatus will reflect Ready=True if the implementation accepts the Realm data +// as valid. +type RealmStatus struct { + duckv1.Status `json:",inline"` +} + +// GetStatus retrieves the status of the Realm. Implements the KRShaped interface. +func (t *Realm) GetStatus() *duckv1.Status { + return &t.Status.Status +} diff --git a/pkg/apis/networking/v1alpha1/realm_types_test.go b/pkg/apis/networking/v1alpha1/realm_types_test.go new file mode 100644 index 000000000..1b36dc171 --- /dev/null +++ b/pkg/apis/networking/v1alpha1/realm_types_test.go @@ -0,0 +1,28 @@ +/* +Copyright 2020 The Knative Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package v1alpha1 + +import "testing" + +func TestRealmGetStatus(t *testing.T) { + r := &Realm{ + Status: RealmStatus{}, + } + + if got, want := r.GetStatus(), &r.Status.Status; got != want { + t.Errorf("GetStatus=%v, want=%v", got, want) + } +} diff --git a/pkg/apis/networking/v1alpha1/realm_validation.go b/pkg/apis/networking/v1alpha1/realm_validation.go new file mode 100644 index 000000000..091e09b5b --- /dev/null +++ b/pkg/apis/networking/v1alpha1/realm_validation.go @@ -0,0 +1,37 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "context" + + "knative.dev/pkg/apis" +) + +// Validate inspects and validates Realm object. +func (r *Realm) Validate(ctx context.Context) *apis.FieldError { + return r.Spec.Validate(apis.WithinSpec(ctx)).ViaField("spec") +} + +// Validate inspects and validates RealmSpec object. +func (spec *RealmSpec) Validate(ctx context.Context) *apis.FieldError { + var all *apis.FieldError + if spec.External == "" && spec.Cluster == "" { + all = all.Also(apis.ErrMissingOneOf("external", "cluster")) + } + return all +} diff --git a/pkg/apis/networking/v1alpha1/realm_validation_test.go b/pkg/apis/networking/v1alpha1/realm_validation_test.go new file mode 100644 index 000000000..609893220 --- /dev/null +++ b/pkg/apis/networking/v1alpha1/realm_validation_test.go @@ -0,0 +1,60 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" +) + +func TestRealmSpecValidation(t *testing.T) { + tests := []struct { + name string + rs RealmSpec + want *apis.FieldError + }{{ + name: "external domain is specified", + rs: RealmSpec{ + External: "test-ext", + }, + }, { + name: "cluster domain is specified", + rs: RealmSpec{ + Cluster: "test-cluster", + }, + }, { + name: "neither cluster nor external domain is specified", + rs: RealmSpec{}, + want: apis.ErrMissingOneOf("spec.cluster", "spec.external"), + }} + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + rs := Realm{ + ObjectMeta: metav1.ObjectMeta{Name: "test-realm"}, + Spec: test.rs, + } + got := rs.Validate(context.Background()) + if !cmp.Equal(test.want.Error(), got.Error()) { + t.Errorf("Validate (-want, +got) = \n%s", cmp.Diff(test.want.Error(), got.Error())) + } + }) + } +} diff --git a/pkg/apis/networking/v1alpha1/register.go b/pkg/apis/networking/v1alpha1/register.go index 2254cac71..9c8cd09db 100644 --- a/pkg/apis/networking/v1alpha1/register.go +++ b/pkg/apis/networking/v1alpha1/register.go @@ -44,8 +44,12 @@ var ( // Adds the list of known types to Scheme. func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, + &Domain{}, + &DomainList{}, &Ingress{}, &IngressList{}, + &Realm{}, + &RealmList{}, &ServerlessService{}, &ServerlessServiceList{}, &Certificate{}, diff --git a/pkg/apis/networking/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/networking/v1alpha1/zz_generated.deepcopy.go index bd179bb46..4d33fafd8 100644 --- a/pkg/apis/networking/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/networking/v1alpha1/zz_generated.deepcopy.go @@ -136,6 +136,110 @@ func (in *CertificateStatus) DeepCopy() *CertificateStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Domain) DeepCopyInto(out *Domain) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Domain. +func (in *Domain) DeepCopy() *Domain { + if in == nil { + return nil + } + out := new(Domain) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Domain) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DomainList) DeepCopyInto(out *DomainList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Domain, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DomainList. +func (in *DomainList) DeepCopy() *DomainList { + if in == nil { + return nil + } + out := new(DomainList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DomainList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DomainSpec) DeepCopyInto(out *DomainSpec) { + *out = *in + if in.LoadBalancers != nil { + in, out := &in.LoadBalancers, &out.LoadBalancers + *out = make([]LoadBalancerIngressSpec, len(*in)) + copy(*out, *in) + } + if in.Configs != nil { + in, out := &in.Configs, &out.Configs + *out = make([]IngressConfig, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DomainSpec. +func (in *DomainSpec) DeepCopy() *DomainSpec { + if in == nil { + return nil + } + out := new(DomainSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DomainStatus) DeepCopyInto(out *DomainStatus) { + *out = *in + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DomainStatus. +func (in *DomainStatus) DeepCopy() *DomainStatus { + if in == nil { + return nil + } + out := new(DomainStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HTTP01Challenge) DeepCopyInto(out *HTTP01Challenge) { *out = *in @@ -334,6 +438,22 @@ func (in *IngressBackendSplit) DeepCopy() *IngressBackendSplit { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IngressConfig) DeepCopyInto(out *IngressConfig) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressConfig. +func (in *IngressConfig) DeepCopy() *IngressConfig { + if in == nil { + return nil + } + out := new(IngressConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IngressList) DeepCopyInto(out *IngressList) { *out = *in @@ -476,6 +596,22 @@ func (in *IngressTLS) DeepCopy() *IngressTLS { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LoadBalancerIngressSpec) DeepCopyInto(out *LoadBalancerIngressSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoadBalancerIngressSpec. +func (in *LoadBalancerIngressSpec) DeepCopy() *LoadBalancerIngressSpec { + if in == nil { + return nil + } + out := new(LoadBalancerIngressSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LoadBalancerIngressStatus) DeepCopyInto(out *LoadBalancerIngressStatus) { *out = *in @@ -513,6 +649,100 @@ func (in *LoadBalancerStatus) DeepCopy() *LoadBalancerStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Realm) DeepCopyInto(out *Realm) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Realm. +func (in *Realm) DeepCopy() *Realm { + if in == nil { + return nil + } + out := new(Realm) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Realm) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RealmList) DeepCopyInto(out *RealmList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Realm, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RealmList. +func (in *RealmList) DeepCopy() *RealmList { + if in == nil { + return nil + } + out := new(RealmList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RealmList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RealmSpec) DeepCopyInto(out *RealmSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RealmSpec. +func (in *RealmSpec) DeepCopy() *RealmSpec { + if in == nil { + return nil + } + out := new(RealmSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RealmStatus) DeepCopyInto(out *RealmStatus) { + *out = *in + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RealmStatus. +func (in *RealmStatus) DeepCopy() *RealmStatus { + if in == nil { + return nil + } + out := new(RealmStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServerlessService) DeepCopyInto(out *ServerlessService) { *out = *in diff --git a/pkg/client/clientset/versioned/typed/networking/v1alpha1/domain.go b/pkg/client/clientset/versioned/typed/networking/v1alpha1/domain.go new file mode 100644 index 000000000..ca2606e97 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/networking/v1alpha1/domain.go @@ -0,0 +1,180 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" + v1alpha1 "knative.dev/networking/pkg/apis/networking/v1alpha1" + scheme "knative.dev/networking/pkg/client/clientset/versioned/scheme" +) + +// DomainsGetter has a method to return a DomainInterface. +// A group's client should implement this interface. +type DomainsGetter interface { + Domains() DomainInterface +} + +// DomainInterface has methods to work with Domain resources. +type DomainInterface interface { + Create(*v1alpha1.Domain) (*v1alpha1.Domain, error) + Update(*v1alpha1.Domain) (*v1alpha1.Domain, error) + UpdateStatus(*v1alpha1.Domain) (*v1alpha1.Domain, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.Domain, error) + List(opts v1.ListOptions) (*v1alpha1.DomainList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Domain, err error) + DomainExpansion +} + +// domains implements DomainInterface +type domains struct { + client rest.Interface +} + +// newDomains returns a Domains +func newDomains(c *NetworkingV1alpha1Client) *domains { + return &domains{ + client: c.RESTClient(), + } +} + +// Get takes name of the domain, and returns the corresponding domain object, and an error if there is any. +func (c *domains) Get(name string, options v1.GetOptions) (result *v1alpha1.Domain, err error) { + result = &v1alpha1.Domain{} + err = c.client.Get(). + Resource("domains"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Domains that match those selectors. +func (c *domains) List(opts v1.ListOptions) (result *v1alpha1.DomainList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.DomainList{} + err = c.client.Get(). + Resource("domains"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested domains. +func (c *domains) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("domains"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a domain and creates it. Returns the server's representation of the domain, and an error, if there is any. +func (c *domains) Create(domain *v1alpha1.Domain) (result *v1alpha1.Domain, err error) { + result = &v1alpha1.Domain{} + err = c.client.Post(). + Resource("domains"). + Body(domain). + Do(). + Into(result) + return +} + +// Update takes the representation of a domain and updates it. Returns the server's representation of the domain, and an error, if there is any. +func (c *domains) Update(domain *v1alpha1.Domain) (result *v1alpha1.Domain, err error) { + result = &v1alpha1.Domain{} + err = c.client.Put(). + Resource("domains"). + Name(domain.Name). + Body(domain). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *domains) UpdateStatus(domain *v1alpha1.Domain) (result *v1alpha1.Domain, err error) { + result = &v1alpha1.Domain{} + err = c.client.Put(). + Resource("domains"). + Name(domain.Name). + SubResource("status"). + Body(domain). + Do(). + Into(result) + return +} + +// Delete takes name of the domain and deletes it. Returns an error if one occurs. +func (c *domains) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Resource("domains"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *domains) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("domains"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched domain. +func (c *domains) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Domain, err error) { + result = &v1alpha1.Domain{} + err = c.client.Patch(pt). + Resource("domains"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/networking/v1alpha1/fake/fake_domain.go b/pkg/client/clientset/versioned/typed/networking/v1alpha1/fake/fake_domain.go new file mode 100644 index 000000000..44dc62145 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/networking/v1alpha1/fake/fake_domain.go @@ -0,0 +1,131 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" + v1alpha1 "knative.dev/networking/pkg/apis/networking/v1alpha1" +) + +// FakeDomains implements DomainInterface +type FakeDomains struct { + Fake *FakeNetworkingV1alpha1 +} + +var domainsResource = schema.GroupVersionResource{Group: "networking.internal.knative.dev", Version: "v1alpha1", Resource: "domains"} + +var domainsKind = schema.GroupVersionKind{Group: "networking.internal.knative.dev", Version: "v1alpha1", Kind: "Domain"} + +// Get takes name of the domain, and returns the corresponding domain object, and an error if there is any. +func (c *FakeDomains) Get(name string, options v1.GetOptions) (result *v1alpha1.Domain, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(domainsResource, name), &v1alpha1.Domain{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Domain), err +} + +// List takes label and field selectors, and returns the list of Domains that match those selectors. +func (c *FakeDomains) List(opts v1.ListOptions) (result *v1alpha1.DomainList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(domainsResource, domainsKind, opts), &v1alpha1.DomainList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.DomainList{ListMeta: obj.(*v1alpha1.DomainList).ListMeta} + for _, item := range obj.(*v1alpha1.DomainList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested domains. +func (c *FakeDomains) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(domainsResource, opts)) +} + +// Create takes the representation of a domain and creates it. Returns the server's representation of the domain, and an error, if there is any. +func (c *FakeDomains) Create(domain *v1alpha1.Domain) (result *v1alpha1.Domain, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(domainsResource, domain), &v1alpha1.Domain{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Domain), err +} + +// Update takes the representation of a domain and updates it. Returns the server's representation of the domain, and an error, if there is any. +func (c *FakeDomains) Update(domain *v1alpha1.Domain) (result *v1alpha1.Domain, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(domainsResource, domain), &v1alpha1.Domain{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Domain), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeDomains) UpdateStatus(domain *v1alpha1.Domain) (*v1alpha1.Domain, error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateSubresourceAction(domainsResource, "status", domain), &v1alpha1.Domain{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Domain), err +} + +// Delete takes name of the domain and deletes it. Returns an error if one occurs. +func (c *FakeDomains) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(domainsResource, name), &v1alpha1.Domain{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeDomains) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(domainsResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha1.DomainList{}) + return err +} + +// Patch applies the patch and returns the patched domain. +func (c *FakeDomains) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Domain, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(domainsResource, name, pt, data, subresources...), &v1alpha1.Domain{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Domain), err +} diff --git a/pkg/client/clientset/versioned/typed/networking/v1alpha1/fake/fake_networking_client.go b/pkg/client/clientset/versioned/typed/networking/v1alpha1/fake/fake_networking_client.go index db1c7f9d8..05d8f2c5b 100644 --- a/pkg/client/clientset/versioned/typed/networking/v1alpha1/fake/fake_networking_client.go +++ b/pkg/client/clientset/versioned/typed/networking/v1alpha1/fake/fake_networking_client.go @@ -32,10 +32,18 @@ func (c *FakeNetworkingV1alpha1) Certificates(namespace string) v1alpha1.Certifi return &FakeCertificates{c, namespace} } +func (c *FakeNetworkingV1alpha1) Domains() v1alpha1.DomainInterface { + return &FakeDomains{c} +} + func (c *FakeNetworkingV1alpha1) Ingresses(namespace string) v1alpha1.IngressInterface { return &FakeIngresses{c, namespace} } +func (c *FakeNetworkingV1alpha1) Realms() v1alpha1.RealmInterface { + return &FakeRealms{c} +} + func (c *FakeNetworkingV1alpha1) ServerlessServices(namespace string) v1alpha1.ServerlessServiceInterface { return &FakeServerlessServices{c, namespace} } diff --git a/pkg/client/clientset/versioned/typed/networking/v1alpha1/fake/fake_realm.go b/pkg/client/clientset/versioned/typed/networking/v1alpha1/fake/fake_realm.go new file mode 100644 index 000000000..2fe35fde3 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/networking/v1alpha1/fake/fake_realm.go @@ -0,0 +1,131 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" + v1alpha1 "knative.dev/networking/pkg/apis/networking/v1alpha1" +) + +// FakeRealms implements RealmInterface +type FakeRealms struct { + Fake *FakeNetworkingV1alpha1 +} + +var realmsResource = schema.GroupVersionResource{Group: "networking.internal.knative.dev", Version: "v1alpha1", Resource: "realms"} + +var realmsKind = schema.GroupVersionKind{Group: "networking.internal.knative.dev", Version: "v1alpha1", Kind: "Realm"} + +// Get takes name of the realm, and returns the corresponding realm object, and an error if there is any. +func (c *FakeRealms) Get(name string, options v1.GetOptions) (result *v1alpha1.Realm, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(realmsResource, name), &v1alpha1.Realm{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Realm), err +} + +// List takes label and field selectors, and returns the list of Realms that match those selectors. +func (c *FakeRealms) List(opts v1.ListOptions) (result *v1alpha1.RealmList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(realmsResource, realmsKind, opts), &v1alpha1.RealmList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.RealmList{ListMeta: obj.(*v1alpha1.RealmList).ListMeta} + for _, item := range obj.(*v1alpha1.RealmList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested realms. +func (c *FakeRealms) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(realmsResource, opts)) +} + +// Create takes the representation of a realm and creates it. Returns the server's representation of the realm, and an error, if there is any. +func (c *FakeRealms) Create(realm *v1alpha1.Realm) (result *v1alpha1.Realm, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(realmsResource, realm), &v1alpha1.Realm{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Realm), err +} + +// Update takes the representation of a realm and updates it. Returns the server's representation of the realm, and an error, if there is any. +func (c *FakeRealms) Update(realm *v1alpha1.Realm) (result *v1alpha1.Realm, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(realmsResource, realm), &v1alpha1.Realm{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Realm), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeRealms) UpdateStatus(realm *v1alpha1.Realm) (*v1alpha1.Realm, error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateSubresourceAction(realmsResource, "status", realm), &v1alpha1.Realm{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Realm), err +} + +// Delete takes name of the realm and deletes it. Returns an error if one occurs. +func (c *FakeRealms) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(realmsResource, name), &v1alpha1.Realm{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeRealms) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(realmsResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha1.RealmList{}) + return err +} + +// Patch applies the patch and returns the patched realm. +func (c *FakeRealms) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Realm, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(realmsResource, name, pt, data, subresources...), &v1alpha1.Realm{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Realm), err +} diff --git a/pkg/client/clientset/versioned/typed/networking/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/networking/v1alpha1/generated_expansion.go index de5c9dac5..0a7454f1d 100644 --- a/pkg/client/clientset/versioned/typed/networking/v1alpha1/generated_expansion.go +++ b/pkg/client/clientset/versioned/typed/networking/v1alpha1/generated_expansion.go @@ -20,6 +20,10 @@ package v1alpha1 type CertificateExpansion interface{} +type DomainExpansion interface{} + type IngressExpansion interface{} +type RealmExpansion interface{} + type ServerlessServiceExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/networking/v1alpha1/networking_client.go b/pkg/client/clientset/versioned/typed/networking/v1alpha1/networking_client.go index 8d45ffe8e..747d9f419 100644 --- a/pkg/client/clientset/versioned/typed/networking/v1alpha1/networking_client.go +++ b/pkg/client/clientset/versioned/typed/networking/v1alpha1/networking_client.go @@ -27,7 +27,9 @@ import ( type NetworkingV1alpha1Interface interface { RESTClient() rest.Interface CertificatesGetter + DomainsGetter IngressesGetter + RealmsGetter ServerlessServicesGetter } @@ -40,10 +42,18 @@ func (c *NetworkingV1alpha1Client) Certificates(namespace string) CertificateInt return newCertificates(c, namespace) } +func (c *NetworkingV1alpha1Client) Domains() DomainInterface { + return newDomains(c) +} + func (c *NetworkingV1alpha1Client) Ingresses(namespace string) IngressInterface { return newIngresses(c, namespace) } +func (c *NetworkingV1alpha1Client) Realms() RealmInterface { + return newRealms(c) +} + func (c *NetworkingV1alpha1Client) ServerlessServices(namespace string) ServerlessServiceInterface { return newServerlessServices(c, namespace) } diff --git a/pkg/client/clientset/versioned/typed/networking/v1alpha1/realm.go b/pkg/client/clientset/versioned/typed/networking/v1alpha1/realm.go new file mode 100644 index 000000000..6ec3f327f --- /dev/null +++ b/pkg/client/clientset/versioned/typed/networking/v1alpha1/realm.go @@ -0,0 +1,180 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" + v1alpha1 "knative.dev/networking/pkg/apis/networking/v1alpha1" + scheme "knative.dev/networking/pkg/client/clientset/versioned/scheme" +) + +// RealmsGetter has a method to return a RealmInterface. +// A group's client should implement this interface. +type RealmsGetter interface { + Realms() RealmInterface +} + +// RealmInterface has methods to work with Realm resources. +type RealmInterface interface { + Create(*v1alpha1.Realm) (*v1alpha1.Realm, error) + Update(*v1alpha1.Realm) (*v1alpha1.Realm, error) + UpdateStatus(*v1alpha1.Realm) (*v1alpha1.Realm, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.Realm, error) + List(opts v1.ListOptions) (*v1alpha1.RealmList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Realm, err error) + RealmExpansion +} + +// realms implements RealmInterface +type realms struct { + client rest.Interface +} + +// newRealms returns a Realms +func newRealms(c *NetworkingV1alpha1Client) *realms { + return &realms{ + client: c.RESTClient(), + } +} + +// Get takes name of the realm, and returns the corresponding realm object, and an error if there is any. +func (c *realms) Get(name string, options v1.GetOptions) (result *v1alpha1.Realm, err error) { + result = &v1alpha1.Realm{} + err = c.client.Get(). + Resource("realms"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Realms that match those selectors. +func (c *realms) List(opts v1.ListOptions) (result *v1alpha1.RealmList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.RealmList{} + err = c.client.Get(). + Resource("realms"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested realms. +func (c *realms) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("realms"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a realm and creates it. Returns the server's representation of the realm, and an error, if there is any. +func (c *realms) Create(realm *v1alpha1.Realm) (result *v1alpha1.Realm, err error) { + result = &v1alpha1.Realm{} + err = c.client.Post(). + Resource("realms"). + Body(realm). + Do(). + Into(result) + return +} + +// Update takes the representation of a realm and updates it. Returns the server's representation of the realm, and an error, if there is any. +func (c *realms) Update(realm *v1alpha1.Realm) (result *v1alpha1.Realm, err error) { + result = &v1alpha1.Realm{} + err = c.client.Put(). + Resource("realms"). + Name(realm.Name). + Body(realm). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *realms) UpdateStatus(realm *v1alpha1.Realm) (result *v1alpha1.Realm, err error) { + result = &v1alpha1.Realm{} + err = c.client.Put(). + Resource("realms"). + Name(realm.Name). + SubResource("status"). + Body(realm). + Do(). + Into(result) + return +} + +// Delete takes name of the realm and deletes it. Returns an error if one occurs. +func (c *realms) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Resource("realms"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *realms) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("realms"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched realm. +func (c *realms) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Realm, err error) { + result = &v1alpha1.Realm{} + err = c.client.Patch(pt). + Resource("realms"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index 3dcfa52db..53fb6baf9 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -55,8 +55,12 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource // Group=networking.internal.knative.dev, Version=v1alpha1 case v1alpha1.SchemeGroupVersion.WithResource("certificates"): return &genericInformer{resource: resource.GroupResource(), informer: f.Networking().V1alpha1().Certificates().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("domains"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Networking().V1alpha1().Domains().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("ingresses"): return &genericInformer{resource: resource.GroupResource(), informer: f.Networking().V1alpha1().Ingresses().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("realms"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Networking().V1alpha1().Realms().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("serverlessservices"): return &genericInformer{resource: resource.GroupResource(), informer: f.Networking().V1alpha1().ServerlessServices().Informer()}, nil diff --git a/pkg/client/informers/externalversions/networking/v1alpha1/domain.go b/pkg/client/informers/externalversions/networking/v1alpha1/domain.go new file mode 100644 index 000000000..190b3cd2c --- /dev/null +++ b/pkg/client/informers/externalversions/networking/v1alpha1/domain.go @@ -0,0 +1,88 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + time "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" + networkingv1alpha1 "knative.dev/networking/pkg/apis/networking/v1alpha1" + versioned "knative.dev/networking/pkg/client/clientset/versioned" + internalinterfaces "knative.dev/networking/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "knative.dev/networking/pkg/client/listers/networking/v1alpha1" +) + +// DomainInformer provides access to a shared informer and lister for +// Domains. +type DomainInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.DomainLister +} + +type domainInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewDomainInformer constructs a new informer for Domain type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewDomainInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredDomainInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredDomainInformer constructs a new informer for Domain type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredDomainInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.NetworkingV1alpha1().Domains().List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.NetworkingV1alpha1().Domains().Watch(options) + }, + }, + &networkingv1alpha1.Domain{}, + resyncPeriod, + indexers, + ) +} + +func (f *domainInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredDomainInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *domainInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&networkingv1alpha1.Domain{}, f.defaultInformer) +} + +func (f *domainInformer) Lister() v1alpha1.DomainLister { + return v1alpha1.NewDomainLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/networking/v1alpha1/interface.go b/pkg/client/informers/externalversions/networking/v1alpha1/interface.go index 613422879..224699564 100644 --- a/pkg/client/informers/externalversions/networking/v1alpha1/interface.go +++ b/pkg/client/informers/externalversions/networking/v1alpha1/interface.go @@ -26,8 +26,12 @@ import ( type Interface interface { // Certificates returns a CertificateInformer. Certificates() CertificateInformer + // Domains returns a DomainInformer. + Domains() DomainInformer // Ingresses returns a IngressInformer. Ingresses() IngressInformer + // Realms returns a RealmInformer. + Realms() RealmInformer // ServerlessServices returns a ServerlessServiceInformer. ServerlessServices() ServerlessServiceInformer } @@ -48,11 +52,21 @@ func (v *version) Certificates() CertificateInformer { return &certificateInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } +// Domains returns a DomainInformer. +func (v *version) Domains() DomainInformer { + return &domainInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} + // Ingresses returns a IngressInformer. func (v *version) Ingresses() IngressInformer { return &ingressInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } +// Realms returns a RealmInformer. +func (v *version) Realms() RealmInformer { + return &realmInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} + // ServerlessServices returns a ServerlessServiceInformer. func (v *version) ServerlessServices() ServerlessServiceInformer { return &serverlessServiceInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} diff --git a/pkg/client/informers/externalversions/networking/v1alpha1/realm.go b/pkg/client/informers/externalversions/networking/v1alpha1/realm.go new file mode 100644 index 000000000..e49afc4b1 --- /dev/null +++ b/pkg/client/informers/externalversions/networking/v1alpha1/realm.go @@ -0,0 +1,88 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + time "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" + networkingv1alpha1 "knative.dev/networking/pkg/apis/networking/v1alpha1" + versioned "knative.dev/networking/pkg/client/clientset/versioned" + internalinterfaces "knative.dev/networking/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "knative.dev/networking/pkg/client/listers/networking/v1alpha1" +) + +// RealmInformer provides access to a shared informer and lister for +// Realms. +type RealmInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.RealmLister +} + +type realmInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewRealmInformer constructs a new informer for Realm type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewRealmInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredRealmInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredRealmInformer constructs a new informer for Realm type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredRealmInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.NetworkingV1alpha1().Realms().List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.NetworkingV1alpha1().Realms().Watch(options) + }, + }, + &networkingv1alpha1.Realm{}, + resyncPeriod, + indexers, + ) +} + +func (f *realmInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredRealmInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *realmInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&networkingv1alpha1.Realm{}, f.defaultInformer) +} + +func (f *realmInformer) Lister() v1alpha1.RealmLister { + return v1alpha1.NewRealmLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/injection/informers/networking/v1alpha1/domain/domain.go b/pkg/client/injection/informers/networking/v1alpha1/domain/domain.go new file mode 100644 index 000000000..ec8472435 --- /dev/null +++ b/pkg/client/injection/informers/networking/v1alpha1/domain/domain.go @@ -0,0 +1,52 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by injection-gen. DO NOT EDIT. + +package domain + +import ( + context "context" + + v1alpha1 "knative.dev/networking/pkg/client/informers/externalversions/networking/v1alpha1" + factory "knative.dev/networking/pkg/client/injection/informers/factory" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" + logging "knative.dev/pkg/logging" +) + +func init() { + injection.Default.RegisterInformer(withInformer) +} + +// Key is used for associating the Informer inside the context.Context. +type Key struct{} + +func withInformer(ctx context.Context) (context.Context, controller.Informer) { + f := factory.Get(ctx) + inf := f.Networking().V1alpha1().Domains() + return context.WithValue(ctx, Key{}, inf), inf.Informer() +} + +// Get extracts the typed informer from the context. +func Get(ctx context.Context) v1alpha1.DomainInformer { + untyped := ctx.Value(Key{}) + if untyped == nil { + logging.FromContext(ctx).Panic( + "Unable to fetch knative.dev/networking/pkg/client/informers/externalversions/networking/v1alpha1.DomainInformer from context.") + } + return untyped.(v1alpha1.DomainInformer) +} diff --git a/pkg/client/injection/informers/networking/v1alpha1/domain/fake/fake.go b/pkg/client/injection/informers/networking/v1alpha1/domain/fake/fake.go new file mode 100644 index 000000000..4b52bd867 --- /dev/null +++ b/pkg/client/injection/informers/networking/v1alpha1/domain/fake/fake.go @@ -0,0 +1,40 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by injection-gen. DO NOT EDIT. + +package fake + +import ( + context "context" + + fake "knative.dev/networking/pkg/client/injection/informers/factory/fake" + domain "knative.dev/networking/pkg/client/injection/informers/networking/v1alpha1/domain" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" +) + +var Get = domain.Get + +func init() { + injection.Fake.RegisterInformer(withInformer) +} + +func withInformer(ctx context.Context) (context.Context, controller.Informer) { + f := fake.Get(ctx) + inf := f.Networking().V1alpha1().Domains() + return context.WithValue(ctx, domain.Key{}, inf), inf.Informer() +} diff --git a/pkg/client/injection/informers/networking/v1alpha1/realm/fake/fake.go b/pkg/client/injection/informers/networking/v1alpha1/realm/fake/fake.go new file mode 100644 index 000000000..10e2f6e17 --- /dev/null +++ b/pkg/client/injection/informers/networking/v1alpha1/realm/fake/fake.go @@ -0,0 +1,40 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by injection-gen. DO NOT EDIT. + +package fake + +import ( + context "context" + + fake "knative.dev/networking/pkg/client/injection/informers/factory/fake" + realm "knative.dev/networking/pkg/client/injection/informers/networking/v1alpha1/realm" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" +) + +var Get = realm.Get + +func init() { + injection.Fake.RegisterInformer(withInformer) +} + +func withInformer(ctx context.Context) (context.Context, controller.Informer) { + f := fake.Get(ctx) + inf := f.Networking().V1alpha1().Realms() + return context.WithValue(ctx, realm.Key{}, inf), inf.Informer() +} diff --git a/pkg/client/injection/informers/networking/v1alpha1/realm/realm.go b/pkg/client/injection/informers/networking/v1alpha1/realm/realm.go new file mode 100644 index 000000000..69599b4a8 --- /dev/null +++ b/pkg/client/injection/informers/networking/v1alpha1/realm/realm.go @@ -0,0 +1,52 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by injection-gen. DO NOT EDIT. + +package realm + +import ( + context "context" + + v1alpha1 "knative.dev/networking/pkg/client/informers/externalversions/networking/v1alpha1" + factory "knative.dev/networking/pkg/client/injection/informers/factory" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" + logging "knative.dev/pkg/logging" +) + +func init() { + injection.Default.RegisterInformer(withInformer) +} + +// Key is used for associating the Informer inside the context.Context. +type Key struct{} + +func withInformer(ctx context.Context) (context.Context, controller.Informer) { + f := factory.Get(ctx) + inf := f.Networking().V1alpha1().Realms() + return context.WithValue(ctx, Key{}, inf), inf.Informer() +} + +// Get extracts the typed informer from the context. +func Get(ctx context.Context) v1alpha1.RealmInformer { + untyped := ctx.Value(Key{}) + if untyped == nil { + logging.FromContext(ctx).Panic( + "Unable to fetch knative.dev/networking/pkg/client/informers/externalversions/networking/v1alpha1.RealmInformer from context.") + } + return untyped.(v1alpha1.RealmInformer) +} diff --git a/pkg/client/injection/reconciler/networking/v1alpha1/domain/controller.go b/pkg/client/injection/reconciler/networking/v1alpha1/domain/controller.go new file mode 100644 index 000000000..68604b6d8 --- /dev/null +++ b/pkg/client/injection/reconciler/networking/v1alpha1/domain/controller.go @@ -0,0 +1,139 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by injection-gen. DO NOT EDIT. + +package domain + +import ( + context "context" + fmt "fmt" + reflect "reflect" + strings "strings" + + corev1 "k8s.io/api/core/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + scheme "k8s.io/client-go/kubernetes/scheme" + v1 "k8s.io/client-go/kubernetes/typed/core/v1" + record "k8s.io/client-go/tools/record" + versionedscheme "knative.dev/networking/pkg/client/clientset/versioned/scheme" + client "knative.dev/networking/pkg/client/injection/client" + domain "knative.dev/networking/pkg/client/injection/informers/networking/v1alpha1/domain" + kubeclient "knative.dev/pkg/client/injection/kube/client" + controller "knative.dev/pkg/controller" + logging "knative.dev/pkg/logging" + reconciler "knative.dev/pkg/reconciler" +) + +const ( + defaultControllerAgentName = "domain-controller" + defaultFinalizerName = "domains.networking.internal.knative.dev" +) + +// NewImpl returns a controller.Impl that handles queuing and feeding work from +// the queue through an implementation of controller.Reconciler, delegating to +// the provided Interface and optional Finalizer methods. OptionsFn is used to return +// controller.Options to be used but the internal reconciler. +func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsFn) *controller.Impl { + logger := logging.FromContext(ctx) + + // Check the options function input. It should be 0 or 1. + if len(optionsFns) > 1 { + logger.Fatalf("up to one options function is supported, found %d", len(optionsFns)) + } + + domainInformer := domain.Get(ctx) + + lister := domainInformer.Lister() + + rec := &reconcilerImpl{ + LeaderAwareFuncs: reconciler.LeaderAwareFuncs{ + PromoteFunc: func(bkt reconciler.Bucket, enq func(reconciler.Bucket, types.NamespacedName)) error { + all, err := lister.List(labels.Everything()) + if err != nil { + return err + } + for _, elt := range all { + // TODO: Consider letting users specify a filter in options. + enq(bkt, types.NamespacedName{ + Namespace: elt.GetNamespace(), + Name: elt.GetName(), + }) + } + return nil + }, + }, + Client: client.Get(ctx), + Lister: lister, + reconciler: r, + finalizerName: defaultFinalizerName, + } + + t := reflect.TypeOf(r).Elem() + queueName := fmt.Sprintf("%s.%s", strings.ReplaceAll(t.PkgPath(), "/", "-"), t.Name()) + + impl := controller.NewImpl(rec, logger, queueName) + agentName := defaultControllerAgentName + + // Pass impl to the options. Save any optional results. + for _, fn := range optionsFns { + opts := fn(impl) + if opts.ConfigStore != nil { + rec.configStore = opts.ConfigStore + } + if opts.FinalizerName != "" { + rec.finalizerName = opts.FinalizerName + } + if opts.AgentName != "" { + agentName = opts.AgentName + } + } + + rec.Recorder = createRecorder(ctx, agentName) + + return impl +} + +func createRecorder(ctx context.Context, agentName string) record.EventRecorder { + logger := logging.FromContext(ctx) + + recorder := controller.GetEventRecorder(ctx) + if recorder == nil { + // Create event broadcaster + logger.Debug("Creating event broadcaster") + eventBroadcaster := record.NewBroadcaster() + watches := []watch.Interface{ + eventBroadcaster.StartLogging(logger.Named("event-broadcaster").Infof), + eventBroadcaster.StartRecordingToSink( + &v1.EventSinkImpl{Interface: kubeclient.Get(ctx).CoreV1().Events("")}), + } + recorder = eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: agentName}) + go func() { + <-ctx.Done() + for _, w := range watches { + w.Stop() + } + }() + } + + return recorder +} + +func init() { + versionedscheme.AddToScheme(scheme.Scheme) +} diff --git a/pkg/client/injection/reconciler/networking/v1alpha1/domain/reconciler.go b/pkg/client/injection/reconciler/networking/v1alpha1/domain/reconciler.go new file mode 100644 index 000000000..1f3da8660 --- /dev/null +++ b/pkg/client/injection/reconciler/networking/v1alpha1/domain/reconciler.go @@ -0,0 +1,434 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by injection-gen. DO NOT EDIT. + +package domain + +import ( + context "context" + json "encoding/json" + fmt "fmt" + reflect "reflect" + + zap "go.uber.org/zap" + v1 "k8s.io/api/core/v1" + equality "k8s.io/apimachinery/pkg/api/equality" + errors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + sets "k8s.io/apimachinery/pkg/util/sets" + cache "k8s.io/client-go/tools/cache" + record "k8s.io/client-go/tools/record" + v1alpha1 "knative.dev/networking/pkg/apis/networking/v1alpha1" + versioned "knative.dev/networking/pkg/client/clientset/versioned" + networkingv1alpha1 "knative.dev/networking/pkg/client/listers/networking/v1alpha1" + controller "knative.dev/pkg/controller" + logging "knative.dev/pkg/logging" + reconciler "knative.dev/pkg/reconciler" +) + +// Interface defines the strongly typed interfaces to be implemented by a +// controller reconciling v1alpha1.Domain. +type Interface interface { + // ReconcileKind implements custom logic to reconcile v1alpha1.Domain. Any changes + // to the objects .Status or .Finalizers will be propagated to the stored + // object. It is recommended that implementors do not call any update calls + // for the Kind inside of ReconcileKind, it is the responsibility of the calling + // controller to propagate those properties. The resource passed to ReconcileKind + // will always have an empty deletion timestamp. + ReconcileKind(ctx context.Context, o *v1alpha1.Domain) reconciler.Event +} + +// Finalizer defines the strongly typed interfaces to be implemented by a +// controller finalizing v1alpha1.Domain. +type Finalizer interface { + // FinalizeKind implements custom logic to finalize v1alpha1.Domain. Any changes + // to the objects .Status or .Finalizers will be ignored. Returning a nil or + // Normal type reconciler.Event will allow the finalizer to be deleted on + // the resource. The resource passed to FinalizeKind will always have a set + // deletion timestamp. + FinalizeKind(ctx context.Context, o *v1alpha1.Domain) reconciler.Event +} + +// ReadOnlyInterface defines the strongly typed interfaces to be implemented by a +// controller reconciling v1alpha1.Domain if they want to process resources for which +// they are not the leader. +type ReadOnlyInterface interface { + // ObserveKind implements logic to observe v1alpha1.Domain. + // This method should not write to the API. + ObserveKind(ctx context.Context, o *v1alpha1.Domain) reconciler.Event +} + +// ReadOnlyFinalizer defines the strongly typed interfaces to be implemented by a +// controller finalizing v1alpha1.Domain if they want to process tombstoned resources +// even when they are not the leader. Due to the nature of how finalizers are handled +// there are no guarantees that this will be called. +type ReadOnlyFinalizer interface { + // ObserveFinalizeKind implements custom logic to observe the final state of v1alpha1.Domain. + // This method should not write to the API. + ObserveFinalizeKind(ctx context.Context, o *v1alpha1.Domain) reconciler.Event +} + +// reconcilerImpl implements controller.Reconciler for v1alpha1.Domain resources. +type reconcilerImpl struct { + // LeaderAwareFuncs is inlined to help us implement reconciler.LeaderAware + reconciler.LeaderAwareFuncs + + // Client is used to write back status updates. + Client versioned.Interface + + // Listers index properties about resources + Lister networkingv1alpha1.DomainLister + + // Recorder is an event recorder for recording Event resources to the + // Kubernetes API. + Recorder record.EventRecorder + + // configStore allows for decorating a context with config maps. + // +optional + configStore reconciler.ConfigStore + + // reconciler is the implementation of the business logic of the resource. + reconciler Interface + + // finalizerName is the name of the finalizer to reconcile. + finalizerName string +} + +// Check that our Reconciler implements controller.Reconciler +var _ controller.Reconciler = (*reconcilerImpl)(nil) + +// Check that our generated Reconciler is always LeaderAware. +var _ reconciler.LeaderAware = (*reconcilerImpl)(nil) + +func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client versioned.Interface, lister networkingv1alpha1.DomainLister, recorder record.EventRecorder, r Interface, options ...controller.Options) controller.Reconciler { + // Check the options function input. It should be 0 or 1. + if len(options) > 1 { + logger.Fatalf("up to one options struct is supported, found %d", len(options)) + } + + // Fail fast when users inadvertently implement the other LeaderAware interface. + // For the typed reconcilers, Promote shouldn't take any arguments. + if _, ok := r.(reconciler.LeaderAware); ok { + logger.Fatalf("%T implements the incorrect LeaderAware interface. Promote() should not take an argument as genreconciler handles the enqueuing automatically.", r) + } + // TODO: Consider validating when folks implement ReadOnlyFinalizer, but not Finalizer. + + rec := &reconcilerImpl{ + LeaderAwareFuncs: reconciler.LeaderAwareFuncs{ + PromoteFunc: func(bkt reconciler.Bucket, enq func(reconciler.Bucket, types.NamespacedName)) error { + all, err := lister.List(labels.Everything()) + if err != nil { + return err + } + for _, elt := range all { + // TODO: Consider letting users specify a filter in options. + enq(bkt, types.NamespacedName{ + Namespace: elt.GetNamespace(), + Name: elt.GetName(), + }) + } + return nil + }, + }, + Client: client, + Lister: lister, + Recorder: recorder, + reconciler: r, + finalizerName: defaultFinalizerName, + } + + for _, opts := range options { + if opts.ConfigStore != nil { + rec.configStore = opts.ConfigStore + } + if opts.FinalizerName != "" { + rec.finalizerName = opts.FinalizerName + } + } + + return rec +} + +// Reconcile implements controller.Reconciler +func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error { + logger := logging.FromContext(ctx) + + // Convert the namespace/name string into a distinct namespace and name + namespace, name, err := cache.SplitMetaNamespaceKey(key) + if err != nil { + logger.Errorf("invalid resource key: %s", key) + return nil + } + // Establish whether we are the leader for use below. + isLeader := r.IsLeaderFor(types.NamespacedName{ + Namespace: namespace, + Name: name, + }) + roi, isROI := r.reconciler.(ReadOnlyInterface) + rof, isROF := r.reconciler.(ReadOnlyFinalizer) + if !isLeader && !isROI && !isROF { + // If we are not the leader, and we don't implement either ReadOnly + // interface, then take a fast-path out. + return nil + } + + // If configStore is set, attach the frozen configuration to the context. + if r.configStore != nil { + ctx = r.configStore.ToContext(ctx) + } + + // Add the recorder to context. + ctx = controller.WithEventRecorder(ctx, r.Recorder) + + // Get the resource with this namespace/name. + + getter := r.Lister + + original, err := getter.Get(name) + + if errors.IsNotFound(err) { + // The resource may no longer exist, in which case we stop processing. + logger.Debugf("resource %q no longer exists", key) + return nil + } else if err != nil { + return err + } + + // Don't modify the informers copy. + resource := original.DeepCopy() + + var reconcileEvent reconciler.Event + if resource.GetDeletionTimestamp().IsZero() { + if isLeader { + // Append the target method to the logger. + logger = logger.With(zap.String("targetMethod", "ReconcileKind")) + + // Set and update the finalizer on resource if r.reconciler + // implements Finalizer. + if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil { + return fmt.Errorf("failed to set finalizers: %w", err) + } + + reconciler.PreProcessReconcile(ctx, resource) + + // Reconcile this copy of the resource and then write back any status + // updates regardless of whether the reconciliation errored out. + reconcileEvent = r.reconciler.ReconcileKind(ctx, resource) + + reconciler.PostProcessReconcile(ctx, resource, original) + + } else if isROI { + // Append the target method to the logger. + logger = logger.With(zap.String("targetMethod", "ObserveKind")) + + // Observe any changes to this resource, since we are not the leader. + reconcileEvent = roi.ObserveKind(ctx, resource) + } + } else if fin, ok := r.reconciler.(Finalizer); isLeader && ok { + // Append the target method to the logger. + logger = logger.With(zap.String("targetMethod", "FinalizeKind")) + + // For finalizing reconcilers, if this resource being marked for deletion + // and reconciled cleanly (nil or normal event), remove the finalizer. + reconcileEvent = fin.FinalizeKind(ctx, resource) + if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil { + return fmt.Errorf("failed to clear finalizers: %w", err) + } + } else if !isLeader && isROF { + // Append the target method to the logger. + logger = logger.With(zap.String("targetMethod", "ObserveFinalizeKind")) + + // For finalizing reconcilers, just observe when we aren't the leader. + reconcileEvent = rof.ObserveFinalizeKind(ctx, resource) + } + + // Synchronize the status. + if equality.Semantic.DeepEqual(original.Status, resource.Status) { + // If we didn't change anything then don't call updateStatus. + // This is important because the copy we loaded from the injectionInformer's + // cache may be stale and we don't want to overwrite a prior update + // to status with this stale state. + } else if !isLeader { + logger.Warn("Saw status changes when we aren't the leader!") + // TODO: Consider logging the diff at Debug? + } else if err = r.updateStatus(original, resource); err != nil { + logger.Warnw("Failed to update resource status", zap.Error(err)) + r.Recorder.Eventf(resource, v1.EventTypeWarning, "UpdateFailed", + "Failed to update status for %q: %v", resource.Name, err) + return err + } + + // Report the reconciler event, if any. + if reconcileEvent != nil { + var event *reconciler.ReconcilerEvent + if reconciler.EventAs(reconcileEvent, &event) { + logger.Infow("Returned an event", zap.Any("event", reconcileEvent)) + r.Recorder.Eventf(resource, event.EventType, event.Reason, event.Format, event.Args...) + + // the event was wrapped inside an error, consider the reconciliation as failed + if _, isEvent := reconcileEvent.(*reconciler.ReconcilerEvent); !isEvent { + return reconcileEvent + } + return nil + } + + logger.Errorw("Returned an error", zap.Error(reconcileEvent)) + r.Recorder.Event(resource, v1.EventTypeWarning, "InternalError", reconcileEvent.Error()) + return reconcileEvent + } + + return nil +} + +func (r *reconcilerImpl) updateStatus(existing *v1alpha1.Domain, desired *v1alpha1.Domain) error { + existing = existing.DeepCopy() + return reconciler.RetryUpdateConflicts(func(attempts int) (err error) { + // The first iteration tries to use the injectionInformer's state, subsequent attempts fetch the latest state via API. + if attempts > 0 { + + getter := r.Client.NetworkingV1alpha1().Domains() + + existing, err = getter.Get(desired.Name, metav1.GetOptions{}) + if err != nil { + return err + } + } + + // If there's nothing to update, just return. + if reflect.DeepEqual(existing.Status, desired.Status) { + return nil + } + + existing.Status = desired.Status + + updater := r.Client.NetworkingV1alpha1().Domains() + + _, err = updater.UpdateStatus(existing) + return err + }) +} + +// updateFinalizersFiltered will update the Finalizers of the resource. +// TODO: this method could be generic and sync all finalizers. For now it only +// updates defaultFinalizerName or its override. +func (r *reconcilerImpl) updateFinalizersFiltered(ctx context.Context, resource *v1alpha1.Domain) (*v1alpha1.Domain, error) { + + getter := r.Lister + + actual, err := getter.Get(resource.Name) + if err != nil { + return resource, err + } + + // Don't modify the informers copy. + existing := actual.DeepCopy() + + var finalizers []string + + // If there's nothing to update, just return. + existingFinalizers := sets.NewString(existing.Finalizers...) + desiredFinalizers := sets.NewString(resource.Finalizers...) + + if desiredFinalizers.Has(r.finalizerName) { + if existingFinalizers.Has(r.finalizerName) { + // Nothing to do. + return resource, nil + } + // Add the finalizer. + finalizers = append(existing.Finalizers, r.finalizerName) + } else { + if !existingFinalizers.Has(r.finalizerName) { + // Nothing to do. + return resource, nil + } + // Remove the finalizer. + existingFinalizers.Delete(r.finalizerName) + finalizers = existingFinalizers.List() + } + + mergePatch := map[string]interface{}{ + "metadata": map[string]interface{}{ + "finalizers": finalizers, + "resourceVersion": existing.ResourceVersion, + }, + } + + patch, err := json.Marshal(mergePatch) + if err != nil { + return resource, err + } + + patcher := r.Client.NetworkingV1alpha1().Domains() + + resourceName := resource.Name + resource, err = patcher.Patch(resourceName, types.MergePatchType, patch) + if err != nil { + r.Recorder.Eventf(resource, v1.EventTypeWarning, "FinalizerUpdateFailed", + "Failed to update finalizers for %q: %v", resourceName, err) + } else { + r.Recorder.Eventf(resource, v1.EventTypeNormal, "FinalizerUpdate", + "Updated %q finalizers", resource.GetName()) + } + return resource, err +} + +func (r *reconcilerImpl) setFinalizerIfFinalizer(ctx context.Context, resource *v1alpha1.Domain) (*v1alpha1.Domain, error) { + if _, ok := r.reconciler.(Finalizer); !ok { + return resource, nil + } + + finalizers := sets.NewString(resource.Finalizers...) + + // If this resource is not being deleted, mark the finalizer. + if resource.GetDeletionTimestamp().IsZero() { + finalizers.Insert(r.finalizerName) + } + + resource.Finalizers = finalizers.List() + + // Synchronize the finalizers filtered by r.finalizerName. + return r.updateFinalizersFiltered(ctx, resource) +} + +func (r *reconcilerImpl) clearFinalizer(ctx context.Context, resource *v1alpha1.Domain, reconcileEvent reconciler.Event) (*v1alpha1.Domain, error) { + if _, ok := r.reconciler.(Finalizer); !ok { + return resource, nil + } + if resource.GetDeletionTimestamp().IsZero() { + return resource, nil + } + + finalizers := sets.NewString(resource.Finalizers...) + + if reconcileEvent != nil { + var event *reconciler.ReconcilerEvent + if reconciler.EventAs(reconcileEvent, &event) { + if event.EventType == v1.EventTypeNormal { + finalizers.Delete(r.finalizerName) + } + } + } else { + finalizers.Delete(r.finalizerName) + } + + resource.Finalizers = finalizers.List() + + // Synchronize the finalizers filtered by r.finalizerName. + return r.updateFinalizersFiltered(ctx, resource) +} diff --git a/pkg/client/injection/reconciler/networking/v1alpha1/domain/stub/controller.go b/pkg/client/injection/reconciler/networking/v1alpha1/domain/stub/controller.go new file mode 100644 index 000000000..9a2ae0f01 --- /dev/null +++ b/pkg/client/injection/reconciler/networking/v1alpha1/domain/stub/controller.go @@ -0,0 +1,54 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by injection-gen. DO NOT EDIT. + +package domain + +import ( + context "context" + + domain "knative.dev/networking/pkg/client/injection/informers/networking/v1alpha1/domain" + v1alpha1domain "knative.dev/networking/pkg/client/injection/reconciler/networking/v1alpha1/domain" + configmap "knative.dev/pkg/configmap" + controller "knative.dev/pkg/controller" + logging "knative.dev/pkg/logging" +) + +// TODO: PLEASE COPY AND MODIFY THIS FILE AS A STARTING POINT + +// NewController creates a Reconciler for Domain and returns the result of NewImpl. +func NewController( + ctx context.Context, + cmw configmap.Watcher, +) *controller.Impl { + logger := logging.FromContext(ctx) + + domainInformer := domain.Get(ctx) + + // TODO: setup additional informers here. + + r := &Reconciler{} + impl := v1alpha1domain.NewImpl(ctx, r) + + logger.Info("Setting up event handlers.") + + domainInformer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue)) + + // TODO: add additional informer event handlers here. + + return impl +} diff --git a/pkg/client/injection/reconciler/networking/v1alpha1/domain/stub/reconciler.go b/pkg/client/injection/reconciler/networking/v1alpha1/domain/stub/reconciler.go new file mode 100644 index 000000000..4fc17d0e0 --- /dev/null +++ b/pkg/client/injection/reconciler/networking/v1alpha1/domain/stub/reconciler.go @@ -0,0 +1,87 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by injection-gen. DO NOT EDIT. + +package domain + +import ( + context "context" + + v1 "k8s.io/api/core/v1" + v1alpha1 "knative.dev/networking/pkg/apis/networking/v1alpha1" + domain "knative.dev/networking/pkg/client/injection/reconciler/networking/v1alpha1/domain" + reconciler "knative.dev/pkg/reconciler" +) + +// TODO: PLEASE COPY AND MODIFY THIS FILE AS A STARTING POINT + +// newReconciledNormal makes a new reconciler event with event type Normal, and +// reason DomainReconciled. +func newReconciledNormal(namespace, name string) reconciler.Event { + return reconciler.NewEvent(v1.EventTypeNormal, "DomainReconciled", "Domain reconciled: \"%s/%s\"", namespace, name) +} + +// Reconciler implements controller.Reconciler for Domain resources. +type Reconciler struct { + // TODO: add additional requirements here. +} + +// Check that our Reconciler implements Interface +var _ domain.Interface = (*Reconciler)(nil) + +// Optionally check that our Reconciler implements Finalizer +//var _ domain.Finalizer = (*Reconciler)(nil) + +// Optionally check that our Reconciler implements ReadOnlyInterface +// Implement this to observe resources even when we are not the leader. +//var _ domain.ReadOnlyInterface = (*Reconciler)(nil) + +// Optionally check that our Reconciler implements ReadOnlyFinalizer +// Implement this to observe tombstoned resources even when we are not +// the leader (best effort). +//var _ domain.ReadOnlyFinalizer = (*Reconciler)(nil) + +// ReconcileKind implements Interface.ReconcileKind. +func (r *Reconciler) ReconcileKind(ctx context.Context, o *v1alpha1.Domain) reconciler.Event { + // TODO: use this if the resource implements InitializeConditions. + // o.Status.InitializeConditions() + + // TODO: add custom reconciliation logic here. + + // TODO: use this if the object has .status.ObservedGeneration. + // o.Status.ObservedGeneration = o.Generation + return newReconciledNormal(o.Namespace, o.Name) +} + +// Optionally, use FinalizeKind to add finalizers. FinalizeKind will be called +// when the resource is deleted. +//func (r *Reconciler) FinalizeKind(ctx context.Context, o *v1alpha1.Domain) reconciler.Event { +// // TODO: add custom finalization logic here. +// return nil +//} + +// Optionally, use ObserveKind to observe the resource when we are not the leader. +// func (r *Reconciler) ObserveKind(ctx context.Context, o *v1alpha1.Domain) reconciler.Event { +// // TODO: add custom observation logic here. +// return nil +// } + +// Optionally, use ObserveFinalizeKind to observe resources being finalized when we are no the leader. +//func (r *Reconciler) ObserveFinalizeKind(ctx context.Context, o *v1alpha1.Domain) reconciler.Event { +// // TODO: add custom observation logic here. +// return nil +//} diff --git a/pkg/client/injection/reconciler/networking/v1alpha1/realm/controller.go b/pkg/client/injection/reconciler/networking/v1alpha1/realm/controller.go new file mode 100644 index 000000000..8e33b2612 --- /dev/null +++ b/pkg/client/injection/reconciler/networking/v1alpha1/realm/controller.go @@ -0,0 +1,139 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by injection-gen. DO NOT EDIT. + +package realm + +import ( + context "context" + fmt "fmt" + reflect "reflect" + strings "strings" + + corev1 "k8s.io/api/core/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + scheme "k8s.io/client-go/kubernetes/scheme" + v1 "k8s.io/client-go/kubernetes/typed/core/v1" + record "k8s.io/client-go/tools/record" + versionedscheme "knative.dev/networking/pkg/client/clientset/versioned/scheme" + client "knative.dev/networking/pkg/client/injection/client" + realm "knative.dev/networking/pkg/client/injection/informers/networking/v1alpha1/realm" + kubeclient "knative.dev/pkg/client/injection/kube/client" + controller "knative.dev/pkg/controller" + logging "knative.dev/pkg/logging" + reconciler "knative.dev/pkg/reconciler" +) + +const ( + defaultControllerAgentName = "realm-controller" + defaultFinalizerName = "realms.networking.internal.knative.dev" +) + +// NewImpl returns a controller.Impl that handles queuing and feeding work from +// the queue through an implementation of controller.Reconciler, delegating to +// the provided Interface and optional Finalizer methods. OptionsFn is used to return +// controller.Options to be used but the internal reconciler. +func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsFn) *controller.Impl { + logger := logging.FromContext(ctx) + + // Check the options function input. It should be 0 or 1. + if len(optionsFns) > 1 { + logger.Fatalf("up to one options function is supported, found %d", len(optionsFns)) + } + + realmInformer := realm.Get(ctx) + + lister := realmInformer.Lister() + + rec := &reconcilerImpl{ + LeaderAwareFuncs: reconciler.LeaderAwareFuncs{ + PromoteFunc: func(bkt reconciler.Bucket, enq func(reconciler.Bucket, types.NamespacedName)) error { + all, err := lister.List(labels.Everything()) + if err != nil { + return err + } + for _, elt := range all { + // TODO: Consider letting users specify a filter in options. + enq(bkt, types.NamespacedName{ + Namespace: elt.GetNamespace(), + Name: elt.GetName(), + }) + } + return nil + }, + }, + Client: client.Get(ctx), + Lister: lister, + reconciler: r, + finalizerName: defaultFinalizerName, + } + + t := reflect.TypeOf(r).Elem() + queueName := fmt.Sprintf("%s.%s", strings.ReplaceAll(t.PkgPath(), "/", "-"), t.Name()) + + impl := controller.NewImpl(rec, logger, queueName) + agentName := defaultControllerAgentName + + // Pass impl to the options. Save any optional results. + for _, fn := range optionsFns { + opts := fn(impl) + if opts.ConfigStore != nil { + rec.configStore = opts.ConfigStore + } + if opts.FinalizerName != "" { + rec.finalizerName = opts.FinalizerName + } + if opts.AgentName != "" { + agentName = opts.AgentName + } + } + + rec.Recorder = createRecorder(ctx, agentName) + + return impl +} + +func createRecorder(ctx context.Context, agentName string) record.EventRecorder { + logger := logging.FromContext(ctx) + + recorder := controller.GetEventRecorder(ctx) + if recorder == nil { + // Create event broadcaster + logger.Debug("Creating event broadcaster") + eventBroadcaster := record.NewBroadcaster() + watches := []watch.Interface{ + eventBroadcaster.StartLogging(logger.Named("event-broadcaster").Infof), + eventBroadcaster.StartRecordingToSink( + &v1.EventSinkImpl{Interface: kubeclient.Get(ctx).CoreV1().Events("")}), + } + recorder = eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: agentName}) + go func() { + <-ctx.Done() + for _, w := range watches { + w.Stop() + } + }() + } + + return recorder +} + +func init() { + versionedscheme.AddToScheme(scheme.Scheme) +} diff --git a/pkg/client/injection/reconciler/networking/v1alpha1/realm/reconciler.go b/pkg/client/injection/reconciler/networking/v1alpha1/realm/reconciler.go new file mode 100644 index 000000000..dea8b6e26 --- /dev/null +++ b/pkg/client/injection/reconciler/networking/v1alpha1/realm/reconciler.go @@ -0,0 +1,434 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by injection-gen. DO NOT EDIT. + +package realm + +import ( + context "context" + json "encoding/json" + fmt "fmt" + reflect "reflect" + + zap "go.uber.org/zap" + v1 "k8s.io/api/core/v1" + equality "k8s.io/apimachinery/pkg/api/equality" + errors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + sets "k8s.io/apimachinery/pkg/util/sets" + cache "k8s.io/client-go/tools/cache" + record "k8s.io/client-go/tools/record" + v1alpha1 "knative.dev/networking/pkg/apis/networking/v1alpha1" + versioned "knative.dev/networking/pkg/client/clientset/versioned" + networkingv1alpha1 "knative.dev/networking/pkg/client/listers/networking/v1alpha1" + controller "knative.dev/pkg/controller" + logging "knative.dev/pkg/logging" + reconciler "knative.dev/pkg/reconciler" +) + +// Interface defines the strongly typed interfaces to be implemented by a +// controller reconciling v1alpha1.Realm. +type Interface interface { + // ReconcileKind implements custom logic to reconcile v1alpha1.Realm. Any changes + // to the objects .Status or .Finalizers will be propagated to the stored + // object. It is recommended that implementors do not call any update calls + // for the Kind inside of ReconcileKind, it is the responsibility of the calling + // controller to propagate those properties. The resource passed to ReconcileKind + // will always have an empty deletion timestamp. + ReconcileKind(ctx context.Context, o *v1alpha1.Realm) reconciler.Event +} + +// Finalizer defines the strongly typed interfaces to be implemented by a +// controller finalizing v1alpha1.Realm. +type Finalizer interface { + // FinalizeKind implements custom logic to finalize v1alpha1.Realm. Any changes + // to the objects .Status or .Finalizers will be ignored. Returning a nil or + // Normal type reconciler.Event will allow the finalizer to be deleted on + // the resource. The resource passed to FinalizeKind will always have a set + // deletion timestamp. + FinalizeKind(ctx context.Context, o *v1alpha1.Realm) reconciler.Event +} + +// ReadOnlyInterface defines the strongly typed interfaces to be implemented by a +// controller reconciling v1alpha1.Realm if they want to process resources for which +// they are not the leader. +type ReadOnlyInterface interface { + // ObserveKind implements logic to observe v1alpha1.Realm. + // This method should not write to the API. + ObserveKind(ctx context.Context, o *v1alpha1.Realm) reconciler.Event +} + +// ReadOnlyFinalizer defines the strongly typed interfaces to be implemented by a +// controller finalizing v1alpha1.Realm if they want to process tombstoned resources +// even when they are not the leader. Due to the nature of how finalizers are handled +// there are no guarantees that this will be called. +type ReadOnlyFinalizer interface { + // ObserveFinalizeKind implements custom logic to observe the final state of v1alpha1.Realm. + // This method should not write to the API. + ObserveFinalizeKind(ctx context.Context, o *v1alpha1.Realm) reconciler.Event +} + +// reconcilerImpl implements controller.Reconciler for v1alpha1.Realm resources. +type reconcilerImpl struct { + // LeaderAwareFuncs is inlined to help us implement reconciler.LeaderAware + reconciler.LeaderAwareFuncs + + // Client is used to write back status updates. + Client versioned.Interface + + // Listers index properties about resources + Lister networkingv1alpha1.RealmLister + + // Recorder is an event recorder for recording Event resources to the + // Kubernetes API. + Recorder record.EventRecorder + + // configStore allows for decorating a context with config maps. + // +optional + configStore reconciler.ConfigStore + + // reconciler is the implementation of the business logic of the resource. + reconciler Interface + + // finalizerName is the name of the finalizer to reconcile. + finalizerName string +} + +// Check that our Reconciler implements controller.Reconciler +var _ controller.Reconciler = (*reconcilerImpl)(nil) + +// Check that our generated Reconciler is always LeaderAware. +var _ reconciler.LeaderAware = (*reconcilerImpl)(nil) + +func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client versioned.Interface, lister networkingv1alpha1.RealmLister, recorder record.EventRecorder, r Interface, options ...controller.Options) controller.Reconciler { + // Check the options function input. It should be 0 or 1. + if len(options) > 1 { + logger.Fatalf("up to one options struct is supported, found %d", len(options)) + } + + // Fail fast when users inadvertently implement the other LeaderAware interface. + // For the typed reconcilers, Promote shouldn't take any arguments. + if _, ok := r.(reconciler.LeaderAware); ok { + logger.Fatalf("%T implements the incorrect LeaderAware interface. Promote() should not take an argument as genreconciler handles the enqueuing automatically.", r) + } + // TODO: Consider validating when folks implement ReadOnlyFinalizer, but not Finalizer. + + rec := &reconcilerImpl{ + LeaderAwareFuncs: reconciler.LeaderAwareFuncs{ + PromoteFunc: func(bkt reconciler.Bucket, enq func(reconciler.Bucket, types.NamespacedName)) error { + all, err := lister.List(labels.Everything()) + if err != nil { + return err + } + for _, elt := range all { + // TODO: Consider letting users specify a filter in options. + enq(bkt, types.NamespacedName{ + Namespace: elt.GetNamespace(), + Name: elt.GetName(), + }) + } + return nil + }, + }, + Client: client, + Lister: lister, + Recorder: recorder, + reconciler: r, + finalizerName: defaultFinalizerName, + } + + for _, opts := range options { + if opts.ConfigStore != nil { + rec.configStore = opts.ConfigStore + } + if opts.FinalizerName != "" { + rec.finalizerName = opts.FinalizerName + } + } + + return rec +} + +// Reconcile implements controller.Reconciler +func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error { + logger := logging.FromContext(ctx) + + // Convert the namespace/name string into a distinct namespace and name + namespace, name, err := cache.SplitMetaNamespaceKey(key) + if err != nil { + logger.Errorf("invalid resource key: %s", key) + return nil + } + // Establish whether we are the leader for use below. + isLeader := r.IsLeaderFor(types.NamespacedName{ + Namespace: namespace, + Name: name, + }) + roi, isROI := r.reconciler.(ReadOnlyInterface) + rof, isROF := r.reconciler.(ReadOnlyFinalizer) + if !isLeader && !isROI && !isROF { + // If we are not the leader, and we don't implement either ReadOnly + // interface, then take a fast-path out. + return nil + } + + // If configStore is set, attach the frozen configuration to the context. + if r.configStore != nil { + ctx = r.configStore.ToContext(ctx) + } + + // Add the recorder to context. + ctx = controller.WithEventRecorder(ctx, r.Recorder) + + // Get the resource with this namespace/name. + + getter := r.Lister + + original, err := getter.Get(name) + + if errors.IsNotFound(err) { + // The resource may no longer exist, in which case we stop processing. + logger.Debugf("resource %q no longer exists", key) + return nil + } else if err != nil { + return err + } + + // Don't modify the informers copy. + resource := original.DeepCopy() + + var reconcileEvent reconciler.Event + if resource.GetDeletionTimestamp().IsZero() { + if isLeader { + // Append the target method to the logger. + logger = logger.With(zap.String("targetMethod", "ReconcileKind")) + + // Set and update the finalizer on resource if r.reconciler + // implements Finalizer. + if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil { + return fmt.Errorf("failed to set finalizers: %w", err) + } + + reconciler.PreProcessReconcile(ctx, resource) + + // Reconcile this copy of the resource and then write back any status + // updates regardless of whether the reconciliation errored out. + reconcileEvent = r.reconciler.ReconcileKind(ctx, resource) + + reconciler.PostProcessReconcile(ctx, resource, original) + + } else if isROI { + // Append the target method to the logger. + logger = logger.With(zap.String("targetMethod", "ObserveKind")) + + // Observe any changes to this resource, since we are not the leader. + reconcileEvent = roi.ObserveKind(ctx, resource) + } + } else if fin, ok := r.reconciler.(Finalizer); isLeader && ok { + // Append the target method to the logger. + logger = logger.With(zap.String("targetMethod", "FinalizeKind")) + + // For finalizing reconcilers, if this resource being marked for deletion + // and reconciled cleanly (nil or normal event), remove the finalizer. + reconcileEvent = fin.FinalizeKind(ctx, resource) + if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil { + return fmt.Errorf("failed to clear finalizers: %w", err) + } + } else if !isLeader && isROF { + // Append the target method to the logger. + logger = logger.With(zap.String("targetMethod", "ObserveFinalizeKind")) + + // For finalizing reconcilers, just observe when we aren't the leader. + reconcileEvent = rof.ObserveFinalizeKind(ctx, resource) + } + + // Synchronize the status. + if equality.Semantic.DeepEqual(original.Status, resource.Status) { + // If we didn't change anything then don't call updateStatus. + // This is important because the copy we loaded from the injectionInformer's + // cache may be stale and we don't want to overwrite a prior update + // to status with this stale state. + } else if !isLeader { + logger.Warn("Saw status changes when we aren't the leader!") + // TODO: Consider logging the diff at Debug? + } else if err = r.updateStatus(original, resource); err != nil { + logger.Warnw("Failed to update resource status", zap.Error(err)) + r.Recorder.Eventf(resource, v1.EventTypeWarning, "UpdateFailed", + "Failed to update status for %q: %v", resource.Name, err) + return err + } + + // Report the reconciler event, if any. + if reconcileEvent != nil { + var event *reconciler.ReconcilerEvent + if reconciler.EventAs(reconcileEvent, &event) { + logger.Infow("Returned an event", zap.Any("event", reconcileEvent)) + r.Recorder.Eventf(resource, event.EventType, event.Reason, event.Format, event.Args...) + + // the event was wrapped inside an error, consider the reconciliation as failed + if _, isEvent := reconcileEvent.(*reconciler.ReconcilerEvent); !isEvent { + return reconcileEvent + } + return nil + } + + logger.Errorw("Returned an error", zap.Error(reconcileEvent)) + r.Recorder.Event(resource, v1.EventTypeWarning, "InternalError", reconcileEvent.Error()) + return reconcileEvent + } + + return nil +} + +func (r *reconcilerImpl) updateStatus(existing *v1alpha1.Realm, desired *v1alpha1.Realm) error { + existing = existing.DeepCopy() + return reconciler.RetryUpdateConflicts(func(attempts int) (err error) { + // The first iteration tries to use the injectionInformer's state, subsequent attempts fetch the latest state via API. + if attempts > 0 { + + getter := r.Client.NetworkingV1alpha1().Realms() + + existing, err = getter.Get(desired.Name, metav1.GetOptions{}) + if err != nil { + return err + } + } + + // If there's nothing to update, just return. + if reflect.DeepEqual(existing.Status, desired.Status) { + return nil + } + + existing.Status = desired.Status + + updater := r.Client.NetworkingV1alpha1().Realms() + + _, err = updater.UpdateStatus(existing) + return err + }) +} + +// updateFinalizersFiltered will update the Finalizers of the resource. +// TODO: this method could be generic and sync all finalizers. For now it only +// updates defaultFinalizerName or its override. +func (r *reconcilerImpl) updateFinalizersFiltered(ctx context.Context, resource *v1alpha1.Realm) (*v1alpha1.Realm, error) { + + getter := r.Lister + + actual, err := getter.Get(resource.Name) + if err != nil { + return resource, err + } + + // Don't modify the informers copy. + existing := actual.DeepCopy() + + var finalizers []string + + // If there's nothing to update, just return. + existingFinalizers := sets.NewString(existing.Finalizers...) + desiredFinalizers := sets.NewString(resource.Finalizers...) + + if desiredFinalizers.Has(r.finalizerName) { + if existingFinalizers.Has(r.finalizerName) { + // Nothing to do. + return resource, nil + } + // Add the finalizer. + finalizers = append(existing.Finalizers, r.finalizerName) + } else { + if !existingFinalizers.Has(r.finalizerName) { + // Nothing to do. + return resource, nil + } + // Remove the finalizer. + existingFinalizers.Delete(r.finalizerName) + finalizers = existingFinalizers.List() + } + + mergePatch := map[string]interface{}{ + "metadata": map[string]interface{}{ + "finalizers": finalizers, + "resourceVersion": existing.ResourceVersion, + }, + } + + patch, err := json.Marshal(mergePatch) + if err != nil { + return resource, err + } + + patcher := r.Client.NetworkingV1alpha1().Realms() + + resourceName := resource.Name + resource, err = patcher.Patch(resourceName, types.MergePatchType, patch) + if err != nil { + r.Recorder.Eventf(resource, v1.EventTypeWarning, "FinalizerUpdateFailed", + "Failed to update finalizers for %q: %v", resourceName, err) + } else { + r.Recorder.Eventf(resource, v1.EventTypeNormal, "FinalizerUpdate", + "Updated %q finalizers", resource.GetName()) + } + return resource, err +} + +func (r *reconcilerImpl) setFinalizerIfFinalizer(ctx context.Context, resource *v1alpha1.Realm) (*v1alpha1.Realm, error) { + if _, ok := r.reconciler.(Finalizer); !ok { + return resource, nil + } + + finalizers := sets.NewString(resource.Finalizers...) + + // If this resource is not being deleted, mark the finalizer. + if resource.GetDeletionTimestamp().IsZero() { + finalizers.Insert(r.finalizerName) + } + + resource.Finalizers = finalizers.List() + + // Synchronize the finalizers filtered by r.finalizerName. + return r.updateFinalizersFiltered(ctx, resource) +} + +func (r *reconcilerImpl) clearFinalizer(ctx context.Context, resource *v1alpha1.Realm, reconcileEvent reconciler.Event) (*v1alpha1.Realm, error) { + if _, ok := r.reconciler.(Finalizer); !ok { + return resource, nil + } + if resource.GetDeletionTimestamp().IsZero() { + return resource, nil + } + + finalizers := sets.NewString(resource.Finalizers...) + + if reconcileEvent != nil { + var event *reconciler.ReconcilerEvent + if reconciler.EventAs(reconcileEvent, &event) { + if event.EventType == v1.EventTypeNormal { + finalizers.Delete(r.finalizerName) + } + } + } else { + finalizers.Delete(r.finalizerName) + } + + resource.Finalizers = finalizers.List() + + // Synchronize the finalizers filtered by r.finalizerName. + return r.updateFinalizersFiltered(ctx, resource) +} diff --git a/pkg/client/injection/reconciler/networking/v1alpha1/realm/stub/controller.go b/pkg/client/injection/reconciler/networking/v1alpha1/realm/stub/controller.go new file mode 100644 index 000000000..f9295edf5 --- /dev/null +++ b/pkg/client/injection/reconciler/networking/v1alpha1/realm/stub/controller.go @@ -0,0 +1,54 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by injection-gen. DO NOT EDIT. + +package realm + +import ( + context "context" + + realm "knative.dev/networking/pkg/client/injection/informers/networking/v1alpha1/realm" + v1alpha1realm "knative.dev/networking/pkg/client/injection/reconciler/networking/v1alpha1/realm" + configmap "knative.dev/pkg/configmap" + controller "knative.dev/pkg/controller" + logging "knative.dev/pkg/logging" +) + +// TODO: PLEASE COPY AND MODIFY THIS FILE AS A STARTING POINT + +// NewController creates a Reconciler for Realm and returns the result of NewImpl. +func NewController( + ctx context.Context, + cmw configmap.Watcher, +) *controller.Impl { + logger := logging.FromContext(ctx) + + realmInformer := realm.Get(ctx) + + // TODO: setup additional informers here. + + r := &Reconciler{} + impl := v1alpha1realm.NewImpl(ctx, r) + + logger.Info("Setting up event handlers.") + + realmInformer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue)) + + // TODO: add additional informer event handlers here. + + return impl +} diff --git a/pkg/client/injection/reconciler/networking/v1alpha1/realm/stub/reconciler.go b/pkg/client/injection/reconciler/networking/v1alpha1/realm/stub/reconciler.go new file mode 100644 index 000000000..d251ff9b6 --- /dev/null +++ b/pkg/client/injection/reconciler/networking/v1alpha1/realm/stub/reconciler.go @@ -0,0 +1,87 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by injection-gen. DO NOT EDIT. + +package realm + +import ( + context "context" + + v1 "k8s.io/api/core/v1" + v1alpha1 "knative.dev/networking/pkg/apis/networking/v1alpha1" + realm "knative.dev/networking/pkg/client/injection/reconciler/networking/v1alpha1/realm" + reconciler "knative.dev/pkg/reconciler" +) + +// TODO: PLEASE COPY AND MODIFY THIS FILE AS A STARTING POINT + +// newReconciledNormal makes a new reconciler event with event type Normal, and +// reason RealmReconciled. +func newReconciledNormal(namespace, name string) reconciler.Event { + return reconciler.NewEvent(v1.EventTypeNormal, "RealmReconciled", "Realm reconciled: \"%s/%s\"", namespace, name) +} + +// Reconciler implements controller.Reconciler for Realm resources. +type Reconciler struct { + // TODO: add additional requirements here. +} + +// Check that our Reconciler implements Interface +var _ realm.Interface = (*Reconciler)(nil) + +// Optionally check that our Reconciler implements Finalizer +//var _ realm.Finalizer = (*Reconciler)(nil) + +// Optionally check that our Reconciler implements ReadOnlyInterface +// Implement this to observe resources even when we are not the leader. +//var _ realm.ReadOnlyInterface = (*Reconciler)(nil) + +// Optionally check that our Reconciler implements ReadOnlyFinalizer +// Implement this to observe tombstoned resources even when we are not +// the leader (best effort). +//var _ realm.ReadOnlyFinalizer = (*Reconciler)(nil) + +// ReconcileKind implements Interface.ReconcileKind. +func (r *Reconciler) ReconcileKind(ctx context.Context, o *v1alpha1.Realm) reconciler.Event { + // TODO: use this if the resource implements InitializeConditions. + // o.Status.InitializeConditions() + + // TODO: add custom reconciliation logic here. + + // TODO: use this if the object has .status.ObservedGeneration. + // o.Status.ObservedGeneration = o.Generation + return newReconciledNormal(o.Namespace, o.Name) +} + +// Optionally, use FinalizeKind to add finalizers. FinalizeKind will be called +// when the resource is deleted. +//func (r *Reconciler) FinalizeKind(ctx context.Context, o *v1alpha1.Realm) reconciler.Event { +// // TODO: add custom finalization logic here. +// return nil +//} + +// Optionally, use ObserveKind to observe the resource when we are not the leader. +// func (r *Reconciler) ObserveKind(ctx context.Context, o *v1alpha1.Realm) reconciler.Event { +// // TODO: add custom observation logic here. +// return nil +// } + +// Optionally, use ObserveFinalizeKind to observe resources being finalized when we are no the leader. +//func (r *Reconciler) ObserveFinalizeKind(ctx context.Context, o *v1alpha1.Realm) reconciler.Event { +// // TODO: add custom observation logic here. +// return nil +//} diff --git a/pkg/client/listers/networking/v1alpha1/domain.go b/pkg/client/listers/networking/v1alpha1/domain.go new file mode 100644 index 000000000..cae1444fa --- /dev/null +++ b/pkg/client/listers/networking/v1alpha1/domain.go @@ -0,0 +1,65 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1alpha1 "knative.dev/networking/pkg/apis/networking/v1alpha1" +) + +// DomainLister helps list Domains. +type DomainLister interface { + // List lists all Domains in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.Domain, err error) + // Get retrieves the Domain from the index for a given name. + Get(name string) (*v1alpha1.Domain, error) + DomainListerExpansion +} + +// domainLister implements the DomainLister interface. +type domainLister struct { + indexer cache.Indexer +} + +// NewDomainLister returns a new DomainLister. +func NewDomainLister(indexer cache.Indexer) DomainLister { + return &domainLister{indexer: indexer} +} + +// List lists all Domains in the indexer. +func (s *domainLister) List(selector labels.Selector) (ret []*v1alpha1.Domain, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.Domain)) + }) + return ret, err +} + +// Get retrieves the Domain from the index for a given name. +func (s *domainLister) Get(name string) (*v1alpha1.Domain, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("domain"), name) + } + return obj.(*v1alpha1.Domain), nil +} diff --git a/pkg/client/listers/networking/v1alpha1/expansion_generated.go b/pkg/client/listers/networking/v1alpha1/expansion_generated.go index a31fdafdc..d72b7cfbe 100644 --- a/pkg/client/listers/networking/v1alpha1/expansion_generated.go +++ b/pkg/client/listers/networking/v1alpha1/expansion_generated.go @@ -26,6 +26,10 @@ type CertificateListerExpansion interface{} // CertificateNamespaceLister. type CertificateNamespaceListerExpansion interface{} +// DomainListerExpansion allows custom methods to be added to +// DomainLister. +type DomainListerExpansion interface{} + // IngressListerExpansion allows custom methods to be added to // IngressLister. type IngressListerExpansion interface{} @@ -34,6 +38,10 @@ type IngressListerExpansion interface{} // IngressNamespaceLister. type IngressNamespaceListerExpansion interface{} +// RealmListerExpansion allows custom methods to be added to +// RealmLister. +type RealmListerExpansion interface{} + // ServerlessServiceListerExpansion allows custom methods to be added to // ServerlessServiceLister. type ServerlessServiceListerExpansion interface{} diff --git a/pkg/client/listers/networking/v1alpha1/realm.go b/pkg/client/listers/networking/v1alpha1/realm.go new file mode 100644 index 000000000..bc2a15688 --- /dev/null +++ b/pkg/client/listers/networking/v1alpha1/realm.go @@ -0,0 +1,65 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1alpha1 "knative.dev/networking/pkg/apis/networking/v1alpha1" +) + +// RealmLister helps list Realms. +type RealmLister interface { + // List lists all Realms in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.Realm, err error) + // Get retrieves the Realm from the index for a given name. + Get(name string) (*v1alpha1.Realm, error) + RealmListerExpansion +} + +// realmLister implements the RealmLister interface. +type realmLister struct { + indexer cache.Indexer +} + +// NewRealmLister returns a new RealmLister. +func NewRealmLister(indexer cache.Indexer) RealmLister { + return &realmLister{indexer: indexer} +} + +// List lists all Realms in the indexer. +func (s *realmLister) List(selector labels.Selector) (ret []*v1alpha1.Realm, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.Realm)) + }) + return ret, err +} + +// Get retrieves the Realm from the index for a given name. +func (s *realmLister) Get(name string) (*v1alpha1.Realm, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("realm"), name) + } + return obj.(*v1alpha1.Realm), nil +}