Add v1beta1 version of DomainMapping crd (#11682)

* Add v1beta1 version of DomainMapping crd

* Keep v1alpha1 as storage version for a release

* Update copyright year

* Rerun codegen after rebase

* Regenerate with new boilerplate
This commit is contained in:
Julian Friedman 2021-07-23 15:20:44 +01:00 committed by GitHub
parent 644798277d
commit 5a51323d83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 4083 additions and 2 deletions

View File

@ -31,11 +31,13 @@ import (
"knative.dev/pkg/webhook/resourcesemantics/defaulting"
"knative.dev/pkg/webhook/resourcesemantics/validation"
servingv1alpha1 "knative.dev/serving/pkg/apis/serving/v1alpha1"
servingv1beta1 "knative.dev/serving/pkg/apis/serving/v1beta1"
"knative.dev/serving/pkg/reconciler/domainmapping/config"
)
var types = map[schema.GroupVersionKind]resourcesemantics.GenericCRD{
servingv1alpha1.SchemeGroupVersion.WithKind("DomainMapping"): &servingv1alpha1.DomainMapping{},
servingv1beta1.SchemeGroupVersion.WithKind("DomainMapping"): &servingv1beta1.DomainMapping{},
}
func newDefaultingAdmissionController(ctx context.Context, cmw configmap.Watcher) *controller.Impl {

View File

@ -22,6 +22,122 @@ metadata:
spec:
group: serving.knative.dev
versions:
- name: v1beta1
served: true
storage: false
subresources:
status: {}
additionalPrinterColumns:
- name: URL
type: string
jsonPath: .status.url
- name: Ready
type: string
jsonPath: ".status.conditions[?(@.type=='Ready')].status"
- name: Reason
type: string
jsonPath: ".status.conditions[?(@.type=='Ready')].reason"
"schema":
"openAPIV3Schema":
description: DomainMapping is a mapping from a custom hostname to an Addressable.
type: object
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: 'Spec is the desired state of the DomainMapping. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status'
type: object
required:
- ref
properties:
ref:
description: "Ref specifies the target of the Domain Mapping. \n The object identified by the Ref must be an Addressable with a URL of the form `{name}.{namespace}.{domain}` where `{domain}` is the cluster domain, and `{name}` and `{namespace}` are the name and namespace of a Kubernetes Service. \n This contract is satisfied by Knative types such as Knative Services and Knative Routes, and by Kubernetes Services."
type: object
required:
- kind
- name
properties:
apiVersion:
description: API version of the referent.
type: string
group:
description: 'Group of the API, without the version of the group. This can be used as an alternative to the APIVersion, and then resolved using ResolveGroup. Note: This API is EXPERIMENTAL and might break anytime. For more details: https://github.com/knative/eventing/issues/5086'
type: string
kind:
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
type: string
namespace:
description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ This is optional field, it gets defaulted to the object holding it if left out.'
type: string
tls:
description: TLS allows the DomainMapping to terminate TLS traffic with an existing secret.
type: object
required:
- secretName
properties:
secretName:
description: SecretName is the name of the existing secret used to terminate TLS traffic.
type: string
status:
description: 'Status is the current state of the DomainMapping. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status'
type: object
properties:
address:
description: Address holds the information needed for a DomainMapping to be the target of an event.
type: object
properties:
url:
type: string
annotations:
description: Annotations is additional Status fields for the Resource to save some additional State as well as convey more information to the user. This is roughly akin to Annotations on any k8s resource, just the reconciler conveying richer information outwards.
type: object
additionalProperties:
type: string
conditions:
description: Conditions the latest available observations of a resource's current state.
type: array
items:
description: 'Condition defines a readiness condition for a Knative resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties'
type: object
required:
- status
- type
properties:
lastTransitionTime:
description: LastTransitionTime is the last time the condition transitioned from one status to another. We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic differences (all other things held constant).
type: string
format: date-time
message:
description: A human readable message indicating details about the transition.
type: string
reason:
description: The reason for the condition's last transition.
type: string
severity:
description: Severity with which to treat failures of this type of condition. When this is not specified, it defaults to Error.
type: string
status:
description: Status of the condition, one of True, False, Unknown.
type: string
type:
description: Type of condition.
type: string
observedGeneration:
description: ObservedGeneration is the 'Generation' of the Service that was last processed by the controller.
type: integer
format: int64
url:
description: URL is the URL of this DomainMapping.
type: string
- name: v1alpha1
served: true
storage: true

View File

@ -33,6 +33,7 @@ webhooks:
- serving.knative.dev
apiVersions:
- v1alpha1
- v1beta1
operations:
- CREATE
- UPDATE

View File

@ -36,6 +36,7 @@ webhooks:
- serving.knative.dev
apiVersions:
- v1alpha1
- v1beta1
operations:
- CREATE
- UPDATE

View File

@ -9,6 +9,9 @@
<li>
<a href="#serving.knative.dev%2fv1alpha1">serving.knative.dev/v1alpha1</a>
</li>
<li>
<a href="#serving.knative.dev%2fv1beta1">serving.knative.dev/v1beta1</a>
</li>
</ul>
<h2 id="autoscaling.internal.knative.dev/v1alpha1">autoscaling.internal.knative.dev/v1alpha1</h2>
<div>
@ -2321,6 +2324,310 @@ string
</tbody>
</table>
<hr/>
<h2 id="serving.knative.dev/v1beta1">serving.knative.dev/v1beta1</h2>
<div>
<p>Package v1beta1 contains the v1beta1 versions of the serving apis.
Api versions allow the api contract for a resource to be changed while keeping
backward compatibility by support multiple concurrent versions
of the same resource</p>
</div>
Resource Types:
<ul><li>
<a href="#serving.knative.dev/v1beta1.DomainMapping">DomainMapping</a>
</li></ul>
<h3 id="serving.knative.dev/v1beta1.DomainMapping">DomainMapping
</h3>
<div>
<p>DomainMapping is a mapping from a custom hostname to an Addressable.</p>
</div>
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>apiVersion</code><br/>
string</td>
<td>
<code>
serving.knative.dev/v1beta1
</code>
</td>
</tr>
<tr>
<td>
<code>kind</code><br/>
string
</td>
<td><code>DomainMapping</code></td>
</tr>
<tr>
<td>
<code>metadata</code><br/>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#objectmeta-v1-meta">
Kubernetes meta/v1.ObjectMeta
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>Standard object&rsquo;s metadata.
More info: <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#metadata">https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#metadata</a></p>
Refer to the Kubernetes API documentation for the fields of the
<code>metadata</code> field.
</td>
</tr>
<tr>
<td>
<code>spec</code><br/>
<em>
<a href="#serving.knative.dev/v1beta1.DomainMappingSpec">
DomainMappingSpec
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>Spec is the desired state of the DomainMapping.
More info: <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status">https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status</a></p>
<br/>
<br/>
<table>
<tr>
<td>
<code>ref</code><br/>
<em>
<a href="https://pkg.go.dev/knative.dev/pkg/apis/duck/v1#KReference">
knative.dev/pkg/apis/duck/v1.KReference
</a>
</em>
</td>
<td>
<p>Ref specifies the target of the Domain Mapping.</p>
<p>The object identified by the Ref must be an Addressable with a URL of the
form <code>{name}.{namespace}.{domain}</code> where <code>{domain}</code> is the cluster domain,
and <code>{name}</code> and <code>{namespace}</code> are the name and namespace of a Kubernetes
Service.</p>
<p>This contract is satisfied by Knative types such as Knative Services and
Knative Routes, and by Kubernetes Services.</p>
</td>
</tr>
<tr>
<td>
<code>tls</code><br/>
<em>
<a href="#serving.knative.dev/v1beta1.SecretTLS">
SecretTLS
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>TLS allows the DomainMapping to terminate TLS traffic with an existing secret.</p>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<code>status</code><br/>
<em>
<a href="#serving.knative.dev/v1beta1.DomainMappingStatus">
DomainMappingStatus
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>Status is the current state of the DomainMapping.
More info: <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status">https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status</a></p>
</td>
</tr>
</tbody>
</table>
<h3 id="serving.knative.dev/v1beta1.CannotConvertError">CannotConvertError
</h3>
<div>
<p>CannotConvertError is returned when a field cannot be converted.</p>
</div>
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>Message</code><br/>
<em>
string
</em>
</td>
<td>
</td>
</tr>
<tr>
<td>
<code>Field</code><br/>
<em>
string
</em>
</td>
<td>
</td>
</tr>
</tbody>
</table>
<h3 id="serving.knative.dev/v1beta1.DomainMappingSpec">DomainMappingSpec
</h3>
<p>
(<em>Appears on:</em><a href="#serving.knative.dev/v1beta1.DomainMapping">DomainMapping</a>)
</p>
<div>
<p>DomainMappingSpec describes the DomainMapping the user wishes to exist.</p>
</div>
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>ref</code><br/>
<em>
<a href="https://pkg.go.dev/knative.dev/pkg/apis/duck/v1#KReference">
knative.dev/pkg/apis/duck/v1.KReference
</a>
</em>
</td>
<td>
<p>Ref specifies the target of the Domain Mapping.</p>
<p>The object identified by the Ref must be an Addressable with a URL of the
form <code>{name}.{namespace}.{domain}</code> where <code>{domain}</code> is the cluster domain,
and <code>{name}</code> and <code>{namespace}</code> are the name and namespace of a Kubernetes
Service.</p>
<p>This contract is satisfied by Knative types such as Knative Services and
Knative Routes, and by Kubernetes Services.</p>
</td>
</tr>
<tr>
<td>
<code>tls</code><br/>
<em>
<a href="#serving.knative.dev/v1beta1.SecretTLS">
SecretTLS
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>TLS allows the DomainMapping to terminate TLS traffic with an existing secret.</p>
</td>
</tr>
</tbody>
</table>
<h3 id="serving.knative.dev/v1beta1.DomainMappingStatus">DomainMappingStatus
</h3>
<p>
(<em>Appears on:</em><a href="#serving.knative.dev/v1beta1.DomainMapping">DomainMapping</a>)
</p>
<div>
<p>DomainMappingStatus describes the current state of the DomainMapping.</p>
</div>
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>Status</code><br/>
<em>
<a href="https://pkg.go.dev/knative.dev/pkg/apis/duck/v1#Status">
knative.dev/pkg/apis/duck/v1.Status
</a>
</em>
</td>
<td>
<p>
(Members of <code>Status</code> are embedded into this type.)
</p>
</td>
</tr>
<tr>
<td>
<code>url</code><br/>
<em>
<a href="https://pkg.go.dev/knative.dev/pkg/apis#URL">
knative.dev/pkg/apis.URL
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>URL is the URL of this DomainMapping.</p>
</td>
</tr>
<tr>
<td>
<code>address</code><br/>
<em>
<a href="https://pkg.go.dev/knative.dev/pkg/apis/duck/v1#Addressable">
knative.dev/pkg/apis/duck/v1.Addressable
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>Address holds the information needed for a DomainMapping to be the target of an event.</p>
</td>
</tr>
</tbody>
</table>
<h3 id="serving.knative.dev/v1beta1.SecretTLS">SecretTLS
</h3>
<p>
(<em>Appears on:</em><a href="#serving.knative.dev/v1beta1.DomainMappingSpec">DomainMappingSpec</a>)
</p>
<div>
<p>SecretTLS wrapper for TLS SecretName.</p>
</div>
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>secretName</code><br/>
<em>
string
</em>
</td>
<td>
<p>SecretName is the name of the existing secret used to terminate TLS traffic.</p>
</td>
</tr>
</tbody>
</table>
<hr/>
<p><em>
Generated with <code>gen-crd-api-reference-docs</code>
.

View File

@ -66,7 +66,7 @@ group "Kubernetes Codegen"
# instead of the $GOPATH directly. For normal projects this can be dropped.
${CODEGEN_PKG}/generate-groups.sh "deepcopy,client,informer,lister" \
knative.dev/serving/pkg/client knative.dev/serving/pkg/apis \
"serving:v1 serving:v1alpha1 autoscaling:v1alpha1" \
"serving:v1 serving:v1beta1 serving:v1alpha1 autoscaling:v1alpha1" \
--go-header-file "${boilerplate}"
group "Knative Codegen"
@ -74,7 +74,7 @@ group "Knative Codegen"
# Knative Injection
${KNATIVE_CODEGEN_PKG}/hack/generate-knative.sh "injection" \
knative.dev/serving/pkg/client knative.dev/serving/pkg/apis \
"serving:v1 serving:v1alpha1 autoscaling:v1alpha1" \
"serving:v1 serving:v1beta1 serving:v1alpha1 autoscaling:v1alpha1" \
--go-header-file "${boilerplate}"
group "Deepcopy Gen"

View File

@ -0,0 +1,9 @@
# Knative Serving API v1beta1
This is the implementation of the Knative Serving API, which is specified in
[`docs/spec/spec.md`](/docs/spec/spec.md) and verified via
[the conformance tests](/test/conformance).
**Updates to this implementation should include a corresponding change to
[the spec](/docs/spec/spec.md) and [the conformance tests](/test/conformance).**
([#780](https://github.com/knative/serving/issues/780))

View File

@ -0,0 +1,51 @@
/*
Copyright 2021 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.
*/
package v1beta1
import (
"fmt"
"knative.dev/pkg/apis"
)
const (
// ConditionTypeConvertible is a Warning condition that is set on
// resources when they cannot be converted to warn of a forthcoming
// breakage.
ConditionTypeConvertible apis.ConditionType = "Convertible"
)
// CannotConvertError is returned when a field cannot be converted.
type CannotConvertError struct {
Message string
Field string
}
var _ error = (*CannotConvertError)(nil)
// Error implements error
func (cce *CannotConvertError) Error() string {
return cce.Message
}
// ConvertErrorf creates a CannotConvertError from the field name and format string.
func ConvertErrorf(field, msg string, args ...interface{}) error {
return &CannotConvertError{
Message: fmt.Sprintf(msg, args...),
Field: field,
}
}

View File

@ -0,0 +1,27 @@
/*
Copyright 2021 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.
*/
package v1beta1
import "testing"
func TestConvertError(t *testing.T) {
ce := ConvertErrorf("field", "foo %v %v %v", "bar", true, 42)
if got, want := ce.Error(), "foo bar true 42"; got != want {
t.Errorf("Error() = %s, wanted %s", got, want)
}
}

View File

@ -0,0 +1,24 @@
/*
Copyright 2021 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.
*/
// +k8s:deepcopy-gen=package
// +groupName=serving.knative.dev
// Package v1beta1 contains the v1beta1 versions of the serving apis.
// Api versions allow the api contract for a resource to be changed while keeping
// backward compatibility by support multiple concurrent versions
// of the same resource
package v1beta1

View File

@ -0,0 +1,36 @@
/*
Copyright 2021 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.
*/
package v1beta1
import (
"context"
"knative.dev/pkg/apis"
"knative.dev/serving/pkg/apis/serving"
)
// SetDefaults implements apis.Defaultable.
func (dm *DomainMapping) SetDefaults(ctx context.Context) {
ctx = apis.WithinParent(ctx, dm.ObjectMeta)
dm.Spec.Ref.SetDefaults(apis.WithinSpec(ctx))
if apis.IsInUpdate(ctx) {
serving.SetUserInfo(ctx, apis.GetBaseline(ctx).(*DomainMapping).Spec, dm.Spec, dm)
} else {
serving.SetUserInfo(ctx, nil, dm.Spec, dm)
}
}

View File

@ -0,0 +1,187 @@
/*
Copyright 2021 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.
*/
package v1beta1
import (
"context"
"testing"
"github.com/google/go-cmp/cmp"
authv1 "k8s.io/api/authentication/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"knative.dev/pkg/apis"
duckv1 "knative.dev/pkg/apis/duck/v1"
"knative.dev/serving/pkg/apis/serving"
)
func TestDomainMappingDefaulting(t *testing.T) {
tests := []struct {
name string
in, out *DomainMapping
}{{
name: "empty",
in: &DomainMapping{},
out: &DomainMapping{},
}, {
name: "empty ref namespace",
in: &DomainMapping{
ObjectMeta: metav1.ObjectMeta{
Namespace: "some-namespace",
},
},
out: &DomainMapping{
ObjectMeta: metav1.ObjectMeta{
Namespace: "some-namespace",
},
Spec: DomainMappingSpec{
Ref: duckv1.KReference{
Namespace: "some-namespace",
},
},
},
}, {
name: "explicit ref namespace",
in: &DomainMapping{
ObjectMeta: metav1.ObjectMeta{
Namespace: "some-namespace",
},
Spec: DomainMappingSpec{
Ref: duckv1.KReference{
Namespace: "explicit-namespace",
},
},
},
out: &DomainMapping{
ObjectMeta: metav1.ObjectMeta{
Namespace: "some-namespace",
},
Spec: DomainMappingSpec{
Ref: duckv1.KReference{
Namespace: "explicit-namespace",
},
},
},
}}
for _, test := range tests {
ctx := context.Background()
test.in.SetDefaults(ctx)
if !cmp.Equal(test.out, test.in) {
t.Errorf("SetDefaults (-want, +got):\n%s", cmp.Diff(test.out, test.in))
}
}
}
func TestDomainMappingUserInfo(t *testing.T) {
const (
u1 = "oveja@knative.dev"
u2 = "cabra@knative.dev"
u3 = "vaca@knative.dev"
)
withUserAnns := func(u1, u2 string, s *DomainMapping) *DomainMapping {
a := s.GetAnnotations()
if a == nil {
a = map[string]string{}
s.SetAnnotations(a)
}
a[serving.CreatorAnnotation] = u1
a[serving.UpdaterAnnotation] = u2
return s
}
tests := []struct {
name string
user string
this *DomainMapping
prev *DomainMapping
wantAnns map[string]string
}{{
name: "create-new",
user: u1,
this: &DomainMapping{},
prev: nil,
wantAnns: map[string]string{
serving.CreatorAnnotation: u1,
serving.UpdaterAnnotation: u1,
},
}, {
name: "update-no-diff-new-object",
user: u2,
this: withUserAnns(u1, u1, &DomainMapping{}),
prev: withUserAnns(u1, u1, &DomainMapping{}),
wantAnns: map[string]string{
serving.CreatorAnnotation: u1,
serving.UpdaterAnnotation: u1,
},
}, {
name: "update-diff-old-object",
user: u2,
this: &DomainMapping{
Spec: DomainMappingSpec{
Ref: duckv1.KReference{
Name: "new",
},
},
},
prev: &DomainMapping{
Spec: DomainMappingSpec{
Ref: duckv1.KReference{
Name: "old",
},
},
},
wantAnns: map[string]string{
serving.UpdaterAnnotation: u2,
},
}, {
name: "update-diff-new-object",
user: u3,
this: withUserAnns(u1, u2, &DomainMapping{
Spec: DomainMappingSpec{
Ref: duckv1.KReference{
Name: "new",
},
},
}),
prev: withUserAnns(u1, u2, &DomainMapping{
Spec: DomainMappingSpec{
Ref: duckv1.KReference{
Name: "old",
},
},
}),
wantAnns: map[string]string{
serving.CreatorAnnotation: u1,
serving.UpdaterAnnotation: u3,
},
}}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
ctx := apis.WithUserInfo(context.Background(), &authv1.UserInfo{
Username: test.user,
})
if test.prev != nil {
ctx = apis.WithinUpdate(ctx, test.prev)
test.prev.SetDefaults(ctx)
}
test.this.SetDefaults(ctx)
if got, want := test.this.GetAnnotations(), test.wantAnns; !cmp.Equal(got, want) {
t.Errorf("Annotations = %v, want: %v, diff (-got, +want): %s", got, want, cmp.Diff(got, want))
}
})
}
}

View File

@ -0,0 +1,180 @@
/*
Copyright 2021 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.
*/
package v1beta1
import (
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
netv1alpha1 "knative.dev/networking/pkg/apis/networking/v1alpha1"
"knative.dev/pkg/apis"
)
var domainMappingCondSet = apis.NewLivingConditionSet(
DomainMappingConditionDomainClaimed,
DomainMappingConditionReferenceResolved,
DomainMappingConditionIngressReady,
DomainMappingConditionCertificateProvisioned,
)
// GetConditionSet retrieves the condition set for this resource. Implements the KRShaped interface.
func (*DomainMapping) GetConditionSet() apis.ConditionSet {
return domainMappingCondSet
}
// GetGroupVersionKind returns the GroupVersionKind.
func (dm *DomainMapping) GetGroupVersionKind() schema.GroupVersionKind {
return SchemeGroupVersion.WithKind("DomainMapping")
}
// IsReady returns true if the DomainMapping is ready.
func (dms *DomainMappingStatus) IsReady() bool {
return domainMappingCondSet.Manage(dms).IsHappy()
}
// IsReady returns true if the Status condition DomainMappingConditionReady
// is true and the latest spec has been observed.
func (dm *DomainMapping) IsReady() bool {
dms := dm.Status
return dms.ObservedGeneration == dm.Generation &&
dms.GetCondition(DomainMappingConditionReady).IsTrue()
}
// InitializeConditions sets the initial values to the conditions.
func (dms *DomainMappingStatus) InitializeConditions() {
domainMappingCondSet.Manage(dms).InitializeConditions()
}
const (
// AutoTLSNotEnabledMessage is the message which is set on the
// DomainMappingConditionCertificateProvisioned condition when it is set to True
// because AutoTLS was not enabled.
AutoTLSNotEnabledMessage = "autoTLS is not enabled"
// TLSCertificateProvidedExternally indicates that a TLS secret won't be created or managed
// instead a reference to an existing TLS secret should have been provided in the DomainMapping spec
TLSCertificateProvidedExternally = "TLS certificate was provided externally"
)
// MarkTLSNotEnabled sets DomainMappingConditionCertificateProvisioned to true when
// certificate provisioning was skipped because TLS was not enabled.
func (dms *DomainMappingStatus) MarkTLSNotEnabled(msg string) {
domainMappingCondSet.Manage(dms).MarkTrueWithReason(DomainMappingConditionCertificateProvisioned,
"TLSNotEnabled", msg)
}
func (dms *DomainMappingStatus) MarkCertificateNotRequired(msg string) {
domainMappingCondSet.Manage(dms).MarkTrueWithReason(DomainMappingConditionCertificateProvisioned,
"CertificateExternallyProvided", msg)
}
// MarkCertificateReady marks the DomainMappingConditionCertificateProvisioned
// condition to indicate that the Certificate is ready.
func (dms *DomainMappingStatus) MarkCertificateReady(name string) {
domainMappingCondSet.Manage(dms).MarkTrue(DomainMappingConditionCertificateProvisioned)
}
// MarkCertificateNotReady marks the DomainMappingConditionCertificateProvisioned
// condition to indicate that the Certificate is not ready.
func (dms *DomainMappingStatus) MarkCertificateNotReady(name string) {
domainMappingCondSet.Manage(dms).MarkUnknown(DomainMappingConditionCertificateProvisioned,
"CertificateNotReady",
"Certificate %s is not ready.", name)
}
// MarkCertificateNotOwned changes the DomainMappingConditionCertificateProvisioned
// status to be false with the reason being that there is an existing
// certificate with the name we wanted to use.
func (dms *DomainMappingStatus) MarkCertificateNotOwned(name string) {
domainMappingCondSet.Manage(dms).MarkFalse(DomainMappingConditionCertificateProvisioned,
"CertificateNotOwned",
"There is an existing certificate %s that we don't own.", name)
}
// MarkCertificateProvisionFailed marks the
// DomainMappingConditionCertificateProvisioned condition to indicate that the
// Certificate provisioning failed.
func (dms *DomainMappingStatus) MarkCertificateProvisionFailed(name string) {
domainMappingCondSet.Manage(dms).MarkFalse(DomainMappingConditionCertificateProvisioned,
"CertificateProvisionFailed",
"Certificate %s failed to be provisioned.", name)
}
// MarkHTTPDowngrade sets DomainMappingConditionCertificateProvisioned to true when plain
// HTTP is enabled even when Certificate is not ready.
func (dms *DomainMappingStatus) MarkHTTPDowngrade(name string) {
domainMappingCondSet.Manage(dms).MarkTrueWithReason(DomainMappingConditionCertificateProvisioned,
"HTTPDowngrade",
"Certificate %s is not ready downgrade HTTP.", name)
}
// MarkIngressNotConfigured changes the IngressReady condition to be unknown to reflect
// that the Ingress does not yet have a Status.
func (dms *DomainMappingStatus) MarkIngressNotConfigured() {
domainMappingCondSet.Manage(dms).MarkUnknown(DomainMappingConditionIngressReady,
"IngressNotConfigured", "Ingress has not yet been reconciled.")
}
// MarkDomainClaimed updates the DomainMappingConditionDomainClaimed condition
// to indicate that the domain was successfully claimed.
func (dms *DomainMappingStatus) MarkDomainClaimed() {
domainMappingCondSet.Manage(dms).MarkTrue(DomainMappingConditionDomainClaimed)
}
// MarkDomainClaimNotOwned updates the DomainMappingConditionDomainClaimed
// condition to indicate that the domain is already in use by another
// DomainMapping.
func (dms *DomainMappingStatus) MarkDomainClaimNotOwned() {
domainMappingCondSet.Manage(dms).MarkFalse(DomainMappingConditionDomainClaimed, "DomainAlreadyClaimed",
"The domain name is already in use by another DomainMapping")
}
// MarkDomainClaimFailed updates the DomainMappingConditionDomainClaimed
// condition to indicate that creating the ClusterDomainClaim failed.
func (dms *DomainMappingStatus) MarkDomainClaimFailed(reason string) {
domainMappingCondSet.Manage(dms).MarkFalse(DomainMappingConditionDomainClaimed, "DomainClaimFailed", reason)
}
// MarkReferenceResolved sets the DomainMappingConditionReferenceResolved
// condition to true.
func (dms *DomainMappingStatus) MarkReferenceResolved() {
domainMappingCondSet.Manage(dms).MarkTrue(DomainMappingConditionReferenceResolved)
}
// MarkReferenceNotResolved sets the DomainMappingConditionReferenceResolved
// condition to false.
func (dms *DomainMappingStatus) MarkReferenceNotResolved(reason string) {
domainMappingCondSet.Manage(dms).MarkFalse(DomainMappingConditionReferenceResolved, "ResolveFailed", reason)
}
// PropagateIngressStatus updates the DomainMappingConditionIngressReady
// condition according to the underlying Ingress's status.
func (dms *DomainMappingStatus) PropagateIngressStatus(cs netv1alpha1.IngressStatus) {
cc := cs.GetCondition(netv1alpha1.IngressConditionReady)
if cc == nil {
dms.MarkIngressNotConfigured()
return
}
m := domainMappingCondSet.Manage(dms)
switch cc.Status {
case corev1.ConditionTrue:
m.MarkTrue(DomainMappingConditionIngressReady)
case corev1.ConditionFalse:
m.MarkFalse(DomainMappingConditionIngressReady, cc.Reason, cc.Message)
case corev1.ConditionUnknown:
m.MarkUnknown(DomainMappingConditionIngressReady, cc.Reason, cc.Message)
}
}

View File

@ -0,0 +1,337 @@
/*
Copyright 2021 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.
*/
package v1beta1
import (
"testing"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
netv1alpha1 "knative.dev/networking/pkg/apis/networking/v1alpha1"
"knative.dev/pkg/apis"
"knative.dev/pkg/apis/duck"
duckv1 "knative.dev/pkg/apis/duck/v1"
apistest "knative.dev/pkg/apis/testing"
)
func TestDomainMappingDuckTypes(t *testing.T) {
tests := []struct {
name string
t duck.Implementable
}{{
name: "conditions",
t: &duckv1.Conditions{},
}}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
err := duck.VerifyType(&DomainMapping{}, test.t)
if err != nil {
t.Errorf("VerifyType(DomainMapping, %T) = %v", test.t, err)
}
})
}
}
func TestDomainMappingGetConditionSet(t *testing.T) {
r := &DomainMapping{}
if got, want := r.GetConditionSet().GetTopLevelConditionType(), apis.ConditionReady; got != want {
t.Errorf("GetTopLevelCondition=%v, want=%v", got, want)
}
}
func TestDomainMappingGetGroupVersionKind(t *testing.T) {
r := &DomainMapping{}
want := schema.GroupVersionKind{
Group: "serving.knative.dev",
Version: "v1beta1",
Kind: "DomainMapping",
}
if got := r.GetGroupVersionKind(); got != want {
t.Errorf("got: %v, want: %v", got, want)
}
}
func TestDomainClaimConditions(t *testing.T) {
dms := &DomainMappingStatus{}
dms.InitializeConditions()
dms.MarkTLSNotEnabled("AutoTLS not yet available for DomainMapping")
apistest.CheckConditionOngoing(dms, DomainMappingConditionDomainClaimed, t)
apistest.CheckConditionOngoing(dms, DomainMappingConditionReady, t)
dms.MarkDomainClaimFailed("rejected")
apistest.CheckConditionFailed(dms, DomainMappingConditionDomainClaimed, t)
apistest.CheckConditionFailed(dms, DomainMappingConditionReady, t)
dms.MarkDomainClaimed()
apistest.CheckConditionSucceeded(dms, DomainMappingConditionDomainClaimed, t)
apistest.CheckConditionOngoing(dms, DomainMappingConditionReady, t)
dms.MarkReferenceResolved()
dms.PropagateIngressStatus(netv1alpha1.IngressStatus{
Status: duckv1.Status{
Conditions: duckv1.Conditions{{
Type: netv1alpha1.IngressConditionReady,
Status: corev1.ConditionTrue,
}},
},
})
apistest.CheckConditionSucceeded(dms, DomainMappingConditionReady, t)
dms.MarkDomainClaimNotOwned()
apistest.CheckConditionFailed(dms, DomainMappingConditionDomainClaimed, t)
apistest.CheckConditionFailed(dms, DomainMappingConditionReady, t)
}
func TestReferenceResolvedCondition(t *testing.T) {
dms := &DomainMappingStatus{}
dms.InitializeConditions()
dms.MarkTLSNotEnabled("AutoTLS not yet available for DomainMapping")
apistest.CheckConditionOngoing(dms, DomainMappingConditionReferenceResolved, t)
apistest.CheckConditionOngoing(dms, DomainMappingConditionReady, t)
dms.MarkReferenceNotResolved("can't get no resolution")
apistest.CheckConditionFailed(dms, DomainMappingConditionReferenceResolved, t)
apistest.CheckConditionFailed(dms, DomainMappingConditionReady, t)
dms.MarkReferenceResolved()
apistest.CheckConditionSucceeded(dms, DomainMappingConditionReferenceResolved, t)
apistest.CheckConditionOngoing(dms, DomainMappingConditionReady, t)
dms.MarkDomainClaimed()
dms.PropagateIngressStatus(netv1alpha1.IngressStatus{
Status: duckv1.Status{
Conditions: duckv1.Conditions{{
Type: netv1alpha1.IngressConditionReady,
Status: corev1.ConditionTrue,
}},
},
})
apistest.CheckConditionSucceeded(dms, DomainMappingConditionReady, t)
dms.MarkReferenceNotResolved("still can't get no resolution")
apistest.CheckConditionFailed(dms, DomainMappingConditionReferenceResolved, t)
apistest.CheckConditionFailed(dms, DomainMappingConditionReady, t)
}
func TestCertificateNotReady(t *testing.T) {
dms := &DomainMappingStatus{}
dms.InitializeConditions()
dms.MarkCertificateNotReady("cert pending")
apistest.CheckConditionOngoing(dms, DomainMappingConditionCertificateProvisioned, t)
}
func TestCertificateProvisionFailed(t *testing.T) {
dms := &DomainMappingStatus{}
dms.InitializeConditions()
dms.MarkCertificateProvisionFailed("cert failed")
apistest.CheckConditionFailed(dms, DomainMappingConditionCertificateProvisioned, t)
}
func TestDomainMappingNotOwnCertificate(t *testing.T) {
dms := &DomainMappingStatus{}
dms.InitializeConditions()
dms.MarkCertificateNotOwned("cert not owned")
apistest.CheckConditionFailed(dms, DomainMappingConditionCertificateProvisioned, t)
}
func TestDomainMappingAutoTLSNotEnabled(t *testing.T) {
dms := &DomainMappingStatus{}
dms.InitializeConditions()
dms.MarkTLSNotEnabled(AutoTLSNotEnabledMessage)
apistest.CheckConditionSucceeded(dms, DomainMappingConditionCertificateProvisioned, t)
}
func TestDomainMappingHTTPDowngrade(t *testing.T) {
dms := &DomainMappingStatus{}
dms.InitializeConditions()
dms.MarkHTTPDowngrade("downgraded to HTTP because we can't obtain cert")
apistest.CheckConditionSucceeded(dms, DomainMappingConditionCertificateProvisioned, t)
}
func TestPropagateIngressStatus(t *testing.T) {
dms := &DomainMappingStatus{}
dms.InitializeConditions()
dms.MarkTLSNotEnabled("AutoTLS not yet available for DomainMapping")
apistest.CheckConditionOngoing(dms, DomainMappingConditionIngressReady, t)
apistest.CheckConditionOngoing(dms, DomainMappingConditionReady, t)
dms.PropagateIngressStatus(netv1alpha1.IngressStatus{
Status: duckv1.Status{
Conditions: duckv1.Conditions{{
Type: netv1alpha1.IngressConditionReady,
Status: corev1.ConditionFalse,
}},
},
})
apistest.CheckConditionFailed(dms, DomainMappingConditionIngressReady, t)
apistest.CheckConditionFailed(dms, DomainMappingConditionReady, t)
dms.PropagateIngressStatus(netv1alpha1.IngressStatus{
Status: duckv1.Status{
Conditions: duckv1.Conditions{{
Type: netv1alpha1.IngressConditionReady,
Status: corev1.ConditionTrue,
}},
},
})
apistest.CheckConditionSucceeded(dms, DomainMappingConditionIngressReady, t)
apistest.CheckConditionOngoing(dms, DomainMappingConditionReady, t)
dms.MarkDomainClaimed()
dms.MarkReferenceResolved()
apistest.CheckConditionSucceeded(dms, DomainMappingConditionReady, t)
dms.PropagateIngressStatus(netv1alpha1.IngressStatus{
Status: duckv1.Status{
Conditions: duckv1.Conditions{{
Type: netv1alpha1.IngressConditionReady,
Status: corev1.ConditionUnknown,
}},
},
})
apistest.CheckConditionOngoing(dms, DomainMappingConditionIngressReady, t)
apistest.CheckConditionOngoing(dms, DomainMappingConditionReady, t)
}
func TestDomainMappingIsReady(t *testing.T) {
cases := []struct {
name string
status DomainMappingStatus
isReady bool
}{{
name: "empty status should not be ready",
status: DomainMappingStatus{},
isReady: false,
}, {
name: "Different condition type should not be ready",
status: DomainMappingStatus{
Status: duckv1.Status{
Conditions: duckv1.Conditions{{
Type: DomainMappingConditionIngressReady,
Status: corev1.ConditionTrue,
}},
},
},
isReady: false,
}, {
name: "False condition status should not be ready",
status: DomainMappingStatus{
Status: duckv1.Status{
Conditions: duckv1.Conditions{{
Type: DomainMappingConditionReady,
Status: corev1.ConditionFalse,
}},
},
},
isReady: false,
}, {
name: "Unknown condition status should not be ready",
status: DomainMappingStatus{
Status: duckv1.Status{
Conditions: duckv1.Conditions{{
Type: DomainMappingConditionReady,
Status: corev1.ConditionUnknown,
}},
},
},
isReady: false,
}, {
name: "Missing condition status should not be ready",
status: DomainMappingStatus{
Status: duckv1.Status{
Conditions: duckv1.Conditions{{
Type: DomainMappingConditionReady,
}},
},
},
isReady: false,
}, {
name: "True condition status should be ready",
status: DomainMappingStatus{
Status: duckv1.Status{
Conditions: duckv1.Conditions{{
Type: DomainMappingConditionReady,
Status: corev1.ConditionTrue,
}},
},
},
isReady: true,
}, {
name: "Multiple conditions with ready status should be ready",
status: DomainMappingStatus{
Status: duckv1.Status{
Conditions: duckv1.Conditions{{
Type: DomainMappingConditionIngressReady,
Status: corev1.ConditionTrue,
}, {
Type: DomainMappingConditionReady,
Status: corev1.ConditionTrue,
}},
},
},
isReady: true,
}, {
name: "Multiple conditions with ready status false should not be ready",
status: DomainMappingStatus{
Status: duckv1.Status{
Conditions: duckv1.Conditions{{
Type: DomainMappingConditionIngressReady,
Status: corev1.ConditionTrue,
}, {
Type: DomainMappingConditionReady,
Status: corev1.ConditionFalse,
}},
},
},
isReady: false,
}}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if e, a := tc.isReady, tc.status.IsReady(); e != a {
t.Errorf("%q expected: %v got: %v", tc.name, e, a)
}
dm := &DomainMapping{}
dm.Status = tc.status
if e, a := tc.isReady, dm.IsReady(); e != a {
t.Errorf("%q expected: %v got: %v", tc.name, e, a)
}
dm.Generation = 2
dm.Status.ObservedGeneration = 1
if dm.IsReady() {
t.Error("Expected DomainMapping not to be Ready when ObservedGeneration != Generation")
}
})
}
}

View File

@ -0,0 +1,135 @@
/*
Copyright 2021 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.
*/
package v1beta1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"knative.dev/pkg/apis"
duckv1 "knative.dev/pkg/apis/duck/v1"
)
// +genclient
// +genreconciler
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// DomainMapping is a mapping from a custom hostname to an Addressable.
type DomainMapping struct {
metav1.TypeMeta `json:",inline"`
// Standard object's metadata.
// More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#metadata
// +optional
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec is the desired state of the DomainMapping.
// More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
// +optional
Spec DomainMappingSpec `json:"spec,omitempty"`
// Status is the current state of the DomainMapping.
// More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
// +optional
Status DomainMappingStatus `json:"status,omitempty"`
}
// Verify that DomainMapping adheres to the appropriate interfaces.
var (
// Check that DomainMapping may be validated and defaulted.
_ apis.Validatable = (*DomainMapping)(nil)
_ apis.Defaultable = (*DomainMapping)(nil)
// Check that the type conforms to the duck Knative Resource shape.
_ duckv1.KRShaped = (*DomainMapping)(nil)
)
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// DomainMappingList is a collection of DomainMapping objects.
type DomainMappingList struct {
metav1.TypeMeta `json:",inline"`
// Standard object metadata.
// More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#metadata
// +optional
metav1.ListMeta `json:"metadata,omitempty"`
// Items is the list of DomainMapping objects.
Items []DomainMapping `json:"items"`
}
// SecretTLS wrapper for TLS SecretName.
type SecretTLS struct {
// SecretName is the name of the existing secret used to terminate TLS traffic.
SecretName string `json:"secretName"`
}
// DomainMappingSpec describes the DomainMapping the user wishes to exist.
type DomainMappingSpec struct {
// Ref specifies the target of the Domain Mapping.
//
// The object identified by the Ref must be an Addressable with a URL of the
// form `{name}.{namespace}.{domain}` where `{domain}` is the cluster domain,
// and `{name}` and `{namespace}` are the name and namespace of a Kubernetes
// Service.
//
// This contract is satisfied by Knative types such as Knative Services and
// Knative Routes, and by Kubernetes Services.
Ref duckv1.KReference `json:"ref"`
// TLS allows the DomainMapping to terminate TLS traffic with an existing secret.
// +optional
TLS *SecretTLS `json:"tls,omitempty"`
}
// DomainMappingStatus describes the current state of the DomainMapping.
type DomainMappingStatus struct {
duckv1.Status `json:",inline"`
// URL is the URL of this DomainMapping.
// +optional
URL *apis.URL `json:"url,omitempty"`
// Address holds the information needed for a DomainMapping to be the target of an event.
// +optional
Address *duckv1.Addressable `json:"address,omitempty"`
}
const (
// DomainMappingConditionReady is set when the DomainMapping is configured
// and the Ingress is ready.
DomainMappingConditionReady = apis.ConditionReady
// DomainMappingConditionReferenceResolved reflects whether the Ref
// has been successfully resolved to an existing object.
DomainMappingConditionReferenceResolved apis.ConditionType = "ReferenceResolved"
// DomainMappingConditionIngressReady reflects the readiness of the
// underlying Ingress resource.
DomainMappingConditionIngressReady apis.ConditionType = "IngressReady"
// DomainMappingConditionDomainClaimed reflects that the ClusterDomainClaim
// for this DomainMapping exists, and is owned by this DomainMapping.
DomainMappingConditionDomainClaimed apis.ConditionType = "DomainClaimed"
// DomainMappingConditionCertificateProvisioned is set to False when the
// Knative Certificates fail to be provisioned for the DomainMapping.
DomainMappingConditionCertificateProvisioned apis.ConditionType = "CertificateProvisioned"
)
// GetStatus retrieves the status of the DomainMapping. Implements the KRShaped interface.
func (dm *DomainMapping) GetStatus() *duckv1.Status {
return &dm.Status.Status
}

View File

@ -0,0 +1,37 @@
/*
Copyright 2021 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.
*/
package v1beta1
import (
"testing"
"github.com/google/go-cmp/cmp"
duckv1 "knative.dev/pkg/apis/duck/v1"
)
func TestDomainMappingGetStatus(t *testing.T) {
status := &duckv1.Status{}
config := DomainMapping{
Status: DomainMappingStatus{
Status: *status,
},
}
if !cmp.Equal(config.GetStatus(), status) {
t.Errorf("GetStatus did not retrieve status. Got=%v Want=%v", config.GetStatus(), status)
}
}

View File

@ -0,0 +1,73 @@
/*
Copyright 2021 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.
*/
package v1beta1
import (
"context"
"fmt"
"strings"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
"knative.dev/pkg/apis"
"knative.dev/pkg/network"
"knative.dev/serving/pkg/apis/serving"
)
// Validate makes sure that DomainMapping is properly configured.
func (dm *DomainMapping) Validate(ctx context.Context) *apis.FieldError {
errs := dm.validateMetadata(ctx).ViaField("metadata")
ctx = apis.WithinParent(ctx, dm.ObjectMeta)
errs = errs.Also(dm.Spec.Validate(apis.WithinSpec(ctx)).ViaField("spec"))
return errs
}
// validateMetadata validates the metadata section of a DomainMapping.
func (dm *DomainMapping) validateMetadata(ctx context.Context) (errs *apis.FieldError) {
if dm.GenerateName != "" {
errs = errs.Also(apis.ErrDisallowedFields("generateName"))
}
err := validation.IsFullyQualifiedDomainName(field.NewPath("name"), dm.Name)
if err != nil {
errs = errs.Also(apis.ErrGeneric(fmt.Sprintf(
"invalid name %q: %s", dm.Name, err.ToAggregate()), "name"))
}
clusterLocalDomain := network.GetClusterDomainName()
if strings.HasSuffix(dm.Name, "."+clusterLocalDomain) {
errs = errs.Also(apis.ErrGeneric(
fmt.Sprintf("invalid name %q: must not be a subdomain of cluster local domain %q", dm.Name, clusterLocalDomain), "name"))
}
if apis.IsInUpdate(ctx) {
original := apis.GetBaseline(ctx).(*DomainMapping)
errs = errs.Also(
apis.ValidateCreatorAndModifier(original.Spec, dm.Spec,
original.GetAnnotations(), dm.GetAnnotations(), serving.GroupName).ViaField("annotations"),
)
}
return errs
}
// Validate makes sure the DomainMappingSpec is properly configured.
func (spec *DomainMappingSpec) Validate(ctx context.Context) *apis.FieldError {
return spec.Ref.Validate(ctx).ViaField("ref")
}

View File

@ -0,0 +1,287 @@
/*
Copyright 2021 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.
*/
package v1beta1
import (
"context"
"testing"
"github.com/google/go-cmp/cmp"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"knative.dev/pkg/apis"
duckv1 "knative.dev/pkg/apis/duck/v1"
"knative.dev/serving/pkg/apis/serving"
)
func TestDomainMappingValidation(t *testing.T) {
tests := []struct {
name string
dm *DomainMapping
want *apis.FieldError
}{{
name: "invalid name",
want: apis.ErrGeneric("invalid name \"invalid\": name: Invalid value: \"invalid\": should be a domain with at least two segments separated by dots", "metadata.name"),
dm: &DomainMapping{
ObjectMeta: metav1.ObjectMeta{
Name: "invalid",
Namespace: "ns",
},
Spec: DomainMappingSpec{
Ref: duckv1.KReference{
Name: "some-name.example.com",
APIVersion: "serving.knative.dev/v1",
Kind: "Service",
Namespace: "ns",
},
},
},
}, {
name: "uses GenerateName rather than Name",
want: apis.ErrDisallowedFields("metadata.generateName").Also(
apis.ErrGeneric("invalid name \"\": name: Required value", "metadata.name")),
dm: &DomainMapping{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "cant-use-this",
Namespace: "ns",
},
Spec: DomainMappingSpec{
Ref: duckv1.KReference{
Name: "some-name.example.com",
APIVersion: "serving.knative.dev/v1",
Kind: "Service",
Namespace: "ns",
},
},
},
}, {
name: "ref in wrong namespace",
want: &apis.FieldError{
Paths: []string{"spec.ref.namespace"},
Details: `parent namespace: "good-namespace" does not match ref: "bad-namespace"`,
Message: `mismatched namespaces`,
},
dm: &DomainMapping{
ObjectMeta: metav1.ObjectMeta{
Name: "wrong-ref-ns.example.com",
Namespace: "good-namespace",
},
Spec: DomainMappingSpec{
Ref: duckv1.KReference{
Name: "some-name",
Namespace: "bad-namespace",
APIVersion: "serving.knative.dev/v1",
Kind: "Service",
},
},
},
}, {
name: "ref missing Kind",
want: apis.ErrMissingField("spec.ref.kind"),
dm: &DomainMapping{
ObjectMeta: metav1.ObjectMeta{
Name: "wrong-kind.example.com",
Namespace: "ns",
},
Spec: DomainMappingSpec{
Ref: duckv1.KReference{
Name: "some-name",
Namespace: "ns",
APIVersion: "serving.knative.dev/v1",
},
},
},
}, {
name: "ref missing ApiVersion",
want: apis.ErrMissingField("spec.ref.apiVersion"),
dm: &DomainMapping{
ObjectMeta: metav1.ObjectMeta{
Name: "wrong-version.example.com",
Namespace: "ns",
},
Spec: DomainMappingSpec{
Ref: duckv1.KReference{
Name: "some-name",
Namespace: "ns",
Kind: "Service",
},
},
},
}, {
name: "cluster local domain name",
want: apis.ErrGeneric("invalid name \"notallowed.svc.cluster.local\": must not be a subdomain of cluster local domain \"cluster.local\"", "metadata.name"),
dm: &DomainMapping{
ObjectMeta: metav1.ObjectMeta{
Name: "notallowed.svc.cluster.local",
Namespace: "ns",
},
Spec: DomainMappingSpec{
Ref: duckv1.KReference{
Name: "some-name",
Namespace: "ns",
Kind: "Service",
APIVersion: "serving.knative.dev/v1",
},
},
},
}}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
ctx := context.Background()
got := test.dm.Validate(ctx)
if !cmp.Equal(test.want.Error(), got.Error()) {
t.Errorf("Validate (-want, +got):\n%s", cmp.Diff(test.want.Error(), got.Error()))
}
})
}
}
func TestDomainMappingAnnotationUpdate(t *testing.T) {
const (
u1 = "oveja@knative.dev"
u2 = "cabra@knative.dev"
u3 = "vaca@knative.dev"
)
spec := func(name string) DomainMappingSpec {
return DomainMappingSpec{
Ref: duckv1.KReference{
Name: name,
Namespace: "ns",
Kind: "Service",
APIVersion: "serving.knative.dev/v1",
},
}
}
tests := []struct {
name string
prev *DomainMapping
this *DomainMapping
want *apis.FieldError
}{{
name: "update creator annotation",
this: &DomainMapping{
ObjectMeta: metav1.ObjectMeta{
Name: "valid.example.com",
Namespace: "ns",
Annotations: map[string]string{
serving.CreatorAnnotation: u2,
serving.UpdaterAnnotation: u1,
},
},
Spec: spec("old"),
},
prev: &DomainMapping{
ObjectMeta: metav1.ObjectMeta{
Name: "valid.example.com",
Namespace: "ns",
Annotations: map[string]string{
serving.CreatorAnnotation: u1,
serving.UpdaterAnnotation: u1,
},
},
Spec: spec("old"),
},
want: (&apis.FieldError{Message: "annotation value is immutable",
Paths: []string{serving.CreatorAnnotation}}).ViaField("metadata.annotations"),
}, {
name: "update creator annotation with spec changes",
this: &DomainMapping{
ObjectMeta: metav1.ObjectMeta{
Name: "valid.example.com",
Namespace: "ns",
Annotations: map[string]string{
serving.CreatorAnnotation: u2,
serving.UpdaterAnnotation: u1,
},
},
Spec: spec("new"),
},
prev: &DomainMapping{
ObjectMeta: metav1.ObjectMeta{
Name: "valid.example.com",
Namespace: "ns",
Annotations: map[string]string{
serving.CreatorAnnotation: u1,
serving.UpdaterAnnotation: u1,
},
},
Spec: spec("old"),
},
want: (&apis.FieldError{Message: "annotation value is immutable",
Paths: []string{serving.CreatorAnnotation}}).ViaField("metadata.annotations"),
}, {
name: "update lastModifier annotation without spec changes",
this: &DomainMapping{
ObjectMeta: metav1.ObjectMeta{
Name: "valid.example.com",
Namespace: "ns",
Annotations: map[string]string{
serving.CreatorAnnotation: u1,
serving.UpdaterAnnotation: u2,
},
},
Spec: spec("old"),
},
prev: &DomainMapping{
ObjectMeta: metav1.ObjectMeta{
Name: "valid.example.com",
Namespace: "ns",
Annotations: map[string]string{
serving.CreatorAnnotation: u1,
serving.UpdaterAnnotation: u1,
},
},
Spec: spec("old"),
},
want: apis.ErrInvalidValue(u2, serving.UpdaterAnnotation).ViaField("metadata.annotations"),
}, {
name: "update lastModifier annotation with spec changes",
this: &DomainMapping{
ObjectMeta: metav1.ObjectMeta{
Name: "valid.example.com",
Namespace: "ns",
Annotations: map[string]string{
serving.CreatorAnnotation: u1,
serving.UpdaterAnnotation: u3,
},
},
Spec: spec("new"),
},
prev: &DomainMapping{
ObjectMeta: metav1.ObjectMeta{
Name: "valid.example.com",
Namespace: "ns",
Annotations: map[string]string{
serving.CreatorAnnotation: u1,
serving.UpdaterAnnotation: u1,
},
},
Spec: spec("old"),
},
want: nil,
}}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
ctx := context.Background()
ctx = apis.WithinUpdate(ctx, test.prev)
if diff := cmp.Diff(test.want.Error(), test.this.Validate(ctx).Error()); diff != "" {
t.Error("Validate (-want, +got) =", diff)
}
})
}
}

View File

@ -0,0 +1,55 @@
/*
Copyright 2021 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.
*/
package v1beta1
import (
"knative.dev/serving/pkg/apis/serving"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: serving.GroupName, Version: "v1beta1"}
// Kind takes an unqualified kind and returns back a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
// SchemeBuilder registers the addKnownTypes function.
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
// AddToScheme applies all the stored functions to the scheme.
AddToScheme = SchemeBuilder.AddToScheme
)
// Adds the list of known types to Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&DomainMapping{},
&DomainMappingList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}

View File

@ -0,0 +1,42 @@
/*
Copyright 2021 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.
*/
package v1beta1
import (
"testing"
"k8s.io/apimachinery/pkg/runtime"
)
func TestRegisterHelpers(t *testing.T) {
if got, want := Kind("Revision"), "Revision.serving.knative.dev"; got.String() != want {
t.Errorf("Kind(Revision) = %v, want %v", got.String(), want)
}
if got, want := Resource("Revision"), "Revision.serving.knative.dev"; got.String() != want {
t.Errorf("Resource(Revision) = %v, want %v", got.String(), want)
}
if got, want := SchemeGroupVersion.String(), "serving.knative.dev/v1beta1"; got != want {
t.Errorf("SchemeGroupVersion() = %v, want %v", got, want)
}
scheme := runtime.NewScheme()
if err := addKnownTypes(scheme); err != nil {
t.Error("addKnownTypes() =", err)
}
}

View File

@ -0,0 +1,169 @@
// +build !ignore_autogenerated
/*
Copyright 2021 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 deepcopy-gen. DO NOT EDIT.
package v1beta1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
apis "knative.dev/pkg/apis"
v1 "knative.dev/pkg/apis/duck/v1"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CannotConvertError) DeepCopyInto(out *CannotConvertError) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CannotConvertError.
func (in *CannotConvertError) DeepCopy() *CannotConvertError {
if in == nil {
return nil
}
out := new(CannotConvertError)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DomainMapping) DeepCopyInto(out *DomainMapping) {
*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 DomainMapping.
func (in *DomainMapping) DeepCopy() *DomainMapping {
if in == nil {
return nil
}
out := new(DomainMapping)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *DomainMapping) 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 *DomainMappingList) DeepCopyInto(out *DomainMappingList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]DomainMapping, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DomainMappingList.
func (in *DomainMappingList) DeepCopy() *DomainMappingList {
if in == nil {
return nil
}
out := new(DomainMappingList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *DomainMappingList) 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 *DomainMappingSpec) DeepCopyInto(out *DomainMappingSpec) {
*out = *in
out.Ref = in.Ref
if in.TLS != nil {
in, out := &in.TLS, &out.TLS
*out = new(SecretTLS)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DomainMappingSpec.
func (in *DomainMappingSpec) DeepCopy() *DomainMappingSpec {
if in == nil {
return nil
}
out := new(DomainMappingSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DomainMappingStatus) DeepCopyInto(out *DomainMappingStatus) {
*out = *in
in.Status.DeepCopyInto(&out.Status)
if in.URL != nil {
in, out := &in.URL, &out.URL
*out = new(apis.URL)
(*in).DeepCopyInto(*out)
}
if in.Address != nil {
in, out := &in.Address, &out.Address
*out = new(v1.Addressable)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DomainMappingStatus.
func (in *DomainMappingStatus) DeepCopy() *DomainMappingStatus {
if in == nil {
return nil
}
out := new(DomainMappingStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SecretTLS) DeepCopyInto(out *SecretTLS) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretTLS.
func (in *SecretTLS) DeepCopy() *SecretTLS {
if in == nil {
return nil
}
out := new(SecretTLS)
in.DeepCopyInto(out)
return out
}

View File

@ -27,12 +27,14 @@ import (
autoscalingv1alpha1 "knative.dev/serving/pkg/client/clientset/versioned/typed/autoscaling/v1alpha1"
servingv1 "knative.dev/serving/pkg/client/clientset/versioned/typed/serving/v1"
servingv1alpha1 "knative.dev/serving/pkg/client/clientset/versioned/typed/serving/v1alpha1"
servingv1beta1 "knative.dev/serving/pkg/client/clientset/versioned/typed/serving/v1beta1"
)
type Interface interface {
Discovery() discovery.DiscoveryInterface
AutoscalingV1alpha1() autoscalingv1alpha1.AutoscalingV1alpha1Interface
ServingV1() servingv1.ServingV1Interface
ServingV1beta1() servingv1beta1.ServingV1beta1Interface
ServingV1alpha1() servingv1alpha1.ServingV1alpha1Interface
}
@ -42,6 +44,7 @@ type Clientset struct {
*discovery.DiscoveryClient
autoscalingV1alpha1 *autoscalingv1alpha1.AutoscalingV1alpha1Client
servingV1 *servingv1.ServingV1Client
servingV1beta1 *servingv1beta1.ServingV1beta1Client
servingV1alpha1 *servingv1alpha1.ServingV1alpha1Client
}
@ -55,6 +58,11 @@ func (c *Clientset) ServingV1() servingv1.ServingV1Interface {
return c.servingV1
}
// ServingV1beta1 retrieves the ServingV1beta1Client
func (c *Clientset) ServingV1beta1() servingv1beta1.ServingV1beta1Interface {
return c.servingV1beta1
}
// ServingV1alpha1 retrieves the ServingV1alpha1Client
func (c *Clientset) ServingV1alpha1() servingv1alpha1.ServingV1alpha1Interface {
return c.servingV1alpha1
@ -89,6 +97,10 @@ func NewForConfig(c *rest.Config) (*Clientset, error) {
if err != nil {
return nil, err
}
cs.servingV1beta1, err = servingv1beta1.NewForConfig(&configShallowCopy)
if err != nil {
return nil, err
}
cs.servingV1alpha1, err = servingv1alpha1.NewForConfig(&configShallowCopy)
if err != nil {
return nil, err
@ -107,6 +119,7 @@ func NewForConfigOrDie(c *rest.Config) *Clientset {
var cs Clientset
cs.autoscalingV1alpha1 = autoscalingv1alpha1.NewForConfigOrDie(c)
cs.servingV1 = servingv1.NewForConfigOrDie(c)
cs.servingV1beta1 = servingv1beta1.NewForConfigOrDie(c)
cs.servingV1alpha1 = servingv1alpha1.NewForConfigOrDie(c)
cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c)
@ -118,6 +131,7 @@ func New(c rest.Interface) *Clientset {
var cs Clientset
cs.autoscalingV1alpha1 = autoscalingv1alpha1.New(c)
cs.servingV1 = servingv1.New(c)
cs.servingV1beta1 = servingv1beta1.New(c)
cs.servingV1alpha1 = servingv1alpha1.New(c)
cs.DiscoveryClient = discovery.NewDiscoveryClient(c)

View File

@ -31,6 +31,8 @@ import (
fakeservingv1 "knative.dev/serving/pkg/client/clientset/versioned/typed/serving/v1/fake"
servingv1alpha1 "knative.dev/serving/pkg/client/clientset/versioned/typed/serving/v1alpha1"
fakeservingv1alpha1 "knative.dev/serving/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake"
servingv1beta1 "knative.dev/serving/pkg/client/clientset/versioned/typed/serving/v1beta1"
fakeservingv1beta1 "knative.dev/serving/pkg/client/clientset/versioned/typed/serving/v1beta1/fake"
)
// NewSimpleClientset returns a clientset that will respond with the provided objects.
@ -90,6 +92,11 @@ func (c *Clientset) ServingV1() servingv1.ServingV1Interface {
return &fakeservingv1.FakeServingV1{Fake: &c.Fake}
}
// ServingV1beta1 retrieves the ServingV1beta1Client
func (c *Clientset) ServingV1beta1() servingv1beta1.ServingV1beta1Interface {
return &fakeservingv1beta1.FakeServingV1beta1{Fake: &c.Fake}
}
// ServingV1alpha1 retrieves the ServingV1alpha1Client
func (c *Clientset) ServingV1alpha1() servingv1alpha1.ServingV1alpha1Interface {
return &fakeservingv1alpha1.FakeServingV1alpha1{Fake: &c.Fake}

View File

@ -27,6 +27,7 @@ import (
autoscalingv1alpha1 "knative.dev/serving/pkg/apis/autoscaling/v1alpha1"
servingv1 "knative.dev/serving/pkg/apis/serving/v1"
servingv1alpha1 "knative.dev/serving/pkg/apis/serving/v1alpha1"
servingv1beta1 "knative.dev/serving/pkg/apis/serving/v1beta1"
)
var scheme = runtime.NewScheme()
@ -35,6 +36,7 @@ var codecs = serializer.NewCodecFactory(scheme)
var localSchemeBuilder = runtime.SchemeBuilder{
autoscalingv1alpha1.AddToScheme,
servingv1.AddToScheme,
servingv1beta1.AddToScheme,
servingv1alpha1.AddToScheme,
}

View File

@ -27,6 +27,7 @@ import (
autoscalingv1alpha1 "knative.dev/serving/pkg/apis/autoscaling/v1alpha1"
servingv1 "knative.dev/serving/pkg/apis/serving/v1"
servingv1alpha1 "knative.dev/serving/pkg/apis/serving/v1alpha1"
servingv1beta1 "knative.dev/serving/pkg/apis/serving/v1beta1"
)
var Scheme = runtime.NewScheme()
@ -35,6 +36,7 @@ var ParameterCodec = runtime.NewParameterCodec(Scheme)
var localSchemeBuilder = runtime.SchemeBuilder{
autoscalingv1alpha1.AddToScheme,
servingv1.AddToScheme,
servingv1beta1.AddToScheme,
servingv1alpha1.AddToScheme,
}

View File

@ -0,0 +1,20 @@
/*
Copyright 2021 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.
// This package has the automatically generated typed clients.
package v1beta1

View File

@ -0,0 +1,195 @@
/*
Copyright 2021 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 v1beta1
import (
"context"
"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"
v1beta1 "knative.dev/serving/pkg/apis/serving/v1beta1"
scheme "knative.dev/serving/pkg/client/clientset/versioned/scheme"
)
// DomainMappingsGetter has a method to return a DomainMappingInterface.
// A group's client should implement this interface.
type DomainMappingsGetter interface {
DomainMappings(namespace string) DomainMappingInterface
}
// DomainMappingInterface has methods to work with DomainMapping resources.
type DomainMappingInterface interface {
Create(ctx context.Context, domainMapping *v1beta1.DomainMapping, opts v1.CreateOptions) (*v1beta1.DomainMapping, error)
Update(ctx context.Context, domainMapping *v1beta1.DomainMapping, opts v1.UpdateOptions) (*v1beta1.DomainMapping, error)
UpdateStatus(ctx context.Context, domainMapping *v1beta1.DomainMapping, opts v1.UpdateOptions) (*v1beta1.DomainMapping, error)
Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
Get(ctx context.Context, name string, opts v1.GetOptions) (*v1beta1.DomainMapping, error)
List(ctx context.Context, opts v1.ListOptions) (*v1beta1.DomainMappingList, error)
Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.DomainMapping, err error)
DomainMappingExpansion
}
// domainMappings implements DomainMappingInterface
type domainMappings struct {
client rest.Interface
ns string
}
// newDomainMappings returns a DomainMappings
func newDomainMappings(c *ServingV1beta1Client, namespace string) *domainMappings {
return &domainMappings{
client: c.RESTClient(),
ns: namespace,
}
}
// Get takes name of the domainMapping, and returns the corresponding domainMapping object, and an error if there is any.
func (c *domainMappings) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.DomainMapping, err error) {
result = &v1beta1.DomainMapping{}
err = c.client.Get().
Namespace(c.ns).
Resource("domainmappings").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do(ctx).
Into(result)
return
}
// List takes label and field selectors, and returns the list of DomainMappings that match those selectors.
func (c *domainMappings) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.DomainMappingList, err error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
result = &v1beta1.DomainMappingList{}
err = c.client.Get().
Namespace(c.ns).
Resource("domainmappings").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Do(ctx).
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested domainMappings.
func (c *domainMappings) Watch(ctx context.Context, 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().
Namespace(c.ns).
Resource("domainmappings").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Watch(ctx)
}
// Create takes the representation of a domainMapping and creates it. Returns the server's representation of the domainMapping, and an error, if there is any.
func (c *domainMappings) Create(ctx context.Context, domainMapping *v1beta1.DomainMapping, opts v1.CreateOptions) (result *v1beta1.DomainMapping, err error) {
result = &v1beta1.DomainMapping{}
err = c.client.Post().
Namespace(c.ns).
Resource("domainmappings").
VersionedParams(&opts, scheme.ParameterCodec).
Body(domainMapping).
Do(ctx).
Into(result)
return
}
// Update takes the representation of a domainMapping and updates it. Returns the server's representation of the domainMapping, and an error, if there is any.
func (c *domainMappings) Update(ctx context.Context, domainMapping *v1beta1.DomainMapping, opts v1.UpdateOptions) (result *v1beta1.DomainMapping, err error) {
result = &v1beta1.DomainMapping{}
err = c.client.Put().
Namespace(c.ns).
Resource("domainmappings").
Name(domainMapping.Name).
VersionedParams(&opts, scheme.ParameterCodec).
Body(domainMapping).
Do(ctx).
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 *domainMappings) UpdateStatus(ctx context.Context, domainMapping *v1beta1.DomainMapping, opts v1.UpdateOptions) (result *v1beta1.DomainMapping, err error) {
result = &v1beta1.DomainMapping{}
err = c.client.Put().
Namespace(c.ns).
Resource("domainmappings").
Name(domainMapping.Name).
SubResource("status").
VersionedParams(&opts, scheme.ParameterCodec).
Body(domainMapping).
Do(ctx).
Into(result)
return
}
// Delete takes name of the domainMapping and deletes it. Returns an error if one occurs.
func (c *domainMappings) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("domainmappings").
Name(name).
Body(&opts).
Do(ctx).
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *domainMappings) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
var timeout time.Duration
if listOpts.TimeoutSeconds != nil {
timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
}
return c.client.Delete().
Namespace(c.ns).
Resource("domainmappings").
VersionedParams(&listOpts, scheme.ParameterCodec).
Timeout(timeout).
Body(&opts).
Do(ctx).
Error()
}
// Patch applies the patch and returns the patched domainMapping.
func (c *domainMappings) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.DomainMapping, err error) {
result = &v1beta1.DomainMapping{}
err = c.client.Patch(pt).
Namespace(c.ns).
Resource("domainmappings").
Name(name).
SubResource(subresources...).
VersionedParams(&opts, scheme.ParameterCodec).
Body(data).
Do(ctx).
Into(result)
return
}

View File

@ -0,0 +1,20 @@
/*
Copyright 2021 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 has the automatically generated clients.
package fake

View File

@ -0,0 +1,142 @@
/*
Copyright 2021 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 (
"context"
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"
v1beta1 "knative.dev/serving/pkg/apis/serving/v1beta1"
)
// FakeDomainMappings implements DomainMappingInterface
type FakeDomainMappings struct {
Fake *FakeServingV1beta1
ns string
}
var domainmappingsResource = schema.GroupVersionResource{Group: "serving.knative.dev", Version: "v1beta1", Resource: "domainmappings"}
var domainmappingsKind = schema.GroupVersionKind{Group: "serving.knative.dev", Version: "v1beta1", Kind: "DomainMapping"}
// Get takes name of the domainMapping, and returns the corresponding domainMapping object, and an error if there is any.
func (c *FakeDomainMappings) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.DomainMapping, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetAction(domainmappingsResource, c.ns, name), &v1beta1.DomainMapping{})
if obj == nil {
return nil, err
}
return obj.(*v1beta1.DomainMapping), err
}
// List takes label and field selectors, and returns the list of DomainMappings that match those selectors.
func (c *FakeDomainMappings) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.DomainMappingList, err error) {
obj, err := c.Fake.
Invokes(testing.NewListAction(domainmappingsResource, domainmappingsKind, c.ns, opts), &v1beta1.DomainMappingList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &v1beta1.DomainMappingList{ListMeta: obj.(*v1beta1.DomainMappingList).ListMeta}
for _, item := range obj.(*v1beta1.DomainMappingList).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 domainMappings.
func (c *FakeDomainMappings) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(domainmappingsResource, c.ns, opts))
}
// Create takes the representation of a domainMapping and creates it. Returns the server's representation of the domainMapping, and an error, if there is any.
func (c *FakeDomainMappings) Create(ctx context.Context, domainMapping *v1beta1.DomainMapping, opts v1.CreateOptions) (result *v1beta1.DomainMapping, err error) {
obj, err := c.Fake.
Invokes(testing.NewCreateAction(domainmappingsResource, c.ns, domainMapping), &v1beta1.DomainMapping{})
if obj == nil {
return nil, err
}
return obj.(*v1beta1.DomainMapping), err
}
// Update takes the representation of a domainMapping and updates it. Returns the server's representation of the domainMapping, and an error, if there is any.
func (c *FakeDomainMappings) Update(ctx context.Context, domainMapping *v1beta1.DomainMapping, opts v1.UpdateOptions) (result *v1beta1.DomainMapping, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateAction(domainmappingsResource, c.ns, domainMapping), &v1beta1.DomainMapping{})
if obj == nil {
return nil, err
}
return obj.(*v1beta1.DomainMapping), 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 *FakeDomainMappings) UpdateStatus(ctx context.Context, domainMapping *v1beta1.DomainMapping, opts v1.UpdateOptions) (*v1beta1.DomainMapping, error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateSubresourceAction(domainmappingsResource, "status", c.ns, domainMapping), &v1beta1.DomainMapping{})
if obj == nil {
return nil, err
}
return obj.(*v1beta1.DomainMapping), err
}
// Delete takes name of the domainMapping and deletes it. Returns an error if one occurs.
func (c *FakeDomainMappings) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(domainmappingsResource, c.ns, name), &v1beta1.DomainMapping{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeDomainMappings) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(domainmappingsResource, c.ns, listOpts)
_, err := c.Fake.Invokes(action, &v1beta1.DomainMappingList{})
return err
}
// Patch applies the patch and returns the patched domainMapping.
func (c *FakeDomainMappings) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.DomainMapping, err error) {
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceAction(domainmappingsResource, c.ns, name, pt, data, subresources...), &v1beta1.DomainMapping{})
if obj == nil {
return nil, err
}
return obj.(*v1beta1.DomainMapping), err
}

View File

@ -0,0 +1,40 @@
/*
Copyright 2021 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 (
rest "k8s.io/client-go/rest"
testing "k8s.io/client-go/testing"
v1beta1 "knative.dev/serving/pkg/client/clientset/versioned/typed/serving/v1beta1"
)
type FakeServingV1beta1 struct {
*testing.Fake
}
func (c *FakeServingV1beta1) DomainMappings(namespace string) v1beta1.DomainMappingInterface {
return &FakeDomainMappings{c, namespace}
}
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *FakeServingV1beta1) RESTClient() rest.Interface {
var ret *rest.RESTClient
return ret
}

View File

@ -0,0 +1,21 @@
/*
Copyright 2021 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 v1beta1
type DomainMappingExpansion interface{}

View File

@ -0,0 +1,89 @@
/*
Copyright 2021 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 v1beta1
import (
rest "k8s.io/client-go/rest"
v1beta1 "knative.dev/serving/pkg/apis/serving/v1beta1"
"knative.dev/serving/pkg/client/clientset/versioned/scheme"
)
type ServingV1beta1Interface interface {
RESTClient() rest.Interface
DomainMappingsGetter
}
// ServingV1beta1Client is used to interact with features provided by the serving.knative.dev group.
type ServingV1beta1Client struct {
restClient rest.Interface
}
func (c *ServingV1beta1Client) DomainMappings(namespace string) DomainMappingInterface {
return newDomainMappings(c, namespace)
}
// NewForConfig creates a new ServingV1beta1Client for the given config.
func NewForConfig(c *rest.Config) (*ServingV1beta1Client, error) {
config := *c
if err := setConfigDefaults(&config); err != nil {
return nil, err
}
client, err := rest.RESTClientFor(&config)
if err != nil {
return nil, err
}
return &ServingV1beta1Client{client}, nil
}
// NewForConfigOrDie creates a new ServingV1beta1Client for the given config and
// panics if there is an error in the config.
func NewForConfigOrDie(c *rest.Config) *ServingV1beta1Client {
client, err := NewForConfig(c)
if err != nil {
panic(err)
}
return client
}
// New creates a new ServingV1beta1Client for the given RESTClient.
func New(c rest.Interface) *ServingV1beta1Client {
return &ServingV1beta1Client{c}
}
func setConfigDefaults(config *rest.Config) error {
gv := v1beta1.SchemeGroupVersion
config.GroupVersion = &gv
config.APIPath = "/apis"
config.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
if config.UserAgent == "" {
config.UserAgent = rest.DefaultKubernetesUserAgent()
}
return nil
}
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *ServingV1beta1Client) RESTClient() rest.Interface {
if c == nil {
return nil
}
return c.restClient
}

View File

@ -26,6 +26,7 @@ import (
v1alpha1 "knative.dev/serving/pkg/apis/autoscaling/v1alpha1"
v1 "knative.dev/serving/pkg/apis/serving/v1"
servingv1alpha1 "knative.dev/serving/pkg/apis/serving/v1alpha1"
v1beta1 "knative.dev/serving/pkg/apis/serving/v1beta1"
)
// GenericInformer is type of SharedIndexInformer which will locate and delegate to other
@ -74,6 +75,10 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
case servingv1alpha1.SchemeGroupVersion.WithResource("domainmappings"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Serving().V1alpha1().DomainMappings().Informer()}, nil
// Group=serving.knative.dev, Version=v1beta1
case v1beta1.SchemeGroupVersion.WithResource("domainmappings"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Serving().V1beta1().DomainMappings().Informer()}, nil
}
return nil, fmt.Errorf("no informer found for %v", resource)

View File

@ -22,12 +22,15 @@ import (
internalinterfaces "knative.dev/serving/pkg/client/informers/externalversions/internalinterfaces"
v1 "knative.dev/serving/pkg/client/informers/externalversions/serving/v1"
v1alpha1 "knative.dev/serving/pkg/client/informers/externalversions/serving/v1alpha1"
v1beta1 "knative.dev/serving/pkg/client/informers/externalversions/serving/v1beta1"
)
// Interface provides access to each of this group's versions.
type Interface interface {
// V1 provides access to shared informers for resources in V1.
V1() v1.Interface
// V1beta1 provides access to shared informers for resources in V1beta1.
V1beta1() v1beta1.Interface
// V1alpha1 provides access to shared informers for resources in V1alpha1.
V1alpha1() v1alpha1.Interface
}
@ -48,6 +51,11 @@ func (g *group) V1() v1.Interface {
return v1.New(g.factory, g.namespace, g.tweakListOptions)
}
// V1beta1 returns a new v1beta1.Interface.
func (g *group) V1beta1() v1beta1.Interface {
return v1beta1.New(g.factory, g.namespace, g.tweakListOptions)
}
// V1alpha1 returns a new v1alpha1.Interface.
func (g *group) V1alpha1() v1alpha1.Interface {
return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions)

View File

@ -0,0 +1,90 @@
/*
Copyright 2021 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 v1beta1
import (
"context"
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"
servingv1beta1 "knative.dev/serving/pkg/apis/serving/v1beta1"
versioned "knative.dev/serving/pkg/client/clientset/versioned"
internalinterfaces "knative.dev/serving/pkg/client/informers/externalversions/internalinterfaces"
v1beta1 "knative.dev/serving/pkg/client/listers/serving/v1beta1"
)
// DomainMappingInformer provides access to a shared informer and lister for
// DomainMappings.
type DomainMappingInformer interface {
Informer() cache.SharedIndexInformer
Lister() v1beta1.DomainMappingLister
}
type domainMappingInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
namespace string
}
// NewDomainMappingInformer constructs a new informer for DomainMapping 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 NewDomainMappingInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredDomainMappingInformer(client, namespace, resyncPeriod, indexers, nil)
}
// NewFilteredDomainMappingInformer constructs a new informer for DomainMapping 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 NewFilteredDomainMappingInformer(client versioned.Interface, namespace string, 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.ServingV1beta1().DomainMappings(namespace).List(context.TODO(), options)
},
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.ServingV1beta1().DomainMappings(namespace).Watch(context.TODO(), options)
},
},
&servingv1beta1.DomainMapping{},
resyncPeriod,
indexers,
)
}
func (f *domainMappingInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredDomainMappingInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *domainMappingInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&servingv1beta1.DomainMapping{}, f.defaultInformer)
}
func (f *domainMappingInformer) Lister() v1beta1.DomainMappingLister {
return v1beta1.NewDomainMappingLister(f.Informer().GetIndexer())
}

View File

@ -0,0 +1,45 @@
/*
Copyright 2021 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 v1beta1
import (
internalinterfaces "knative.dev/serving/pkg/client/informers/externalversions/internalinterfaces"
)
// Interface provides access to all the informers in this group version.
type Interface interface {
// DomainMappings returns a DomainMappingInformer.
DomainMappings() DomainMappingInformer
}
type version struct {
factory internalinterfaces.SharedInformerFactory
namespace string
tweakListOptions internalinterfaces.TweakListOptionsFunc
}
// New returns a new Interface.
func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
}
// DomainMappings returns a DomainMappingInformer.
func (v *version) DomainMappings() DomainMappingInformer {
return &domainMappingInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}

View File

@ -0,0 +1,52 @@
/*
Copyright 2021 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 domainmapping
import (
context "context"
controller "knative.dev/pkg/controller"
injection "knative.dev/pkg/injection"
logging "knative.dev/pkg/logging"
v1beta1 "knative.dev/serving/pkg/client/informers/externalversions/serving/v1beta1"
factory "knative.dev/serving/pkg/client/injection/informers/factory"
)
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.Serving().V1beta1().DomainMappings()
return context.WithValue(ctx, Key{}, inf), inf.Informer()
}
// Get extracts the typed informer from the context.
func Get(ctx context.Context) v1beta1.DomainMappingInformer {
untyped := ctx.Value(Key{})
if untyped == nil {
logging.FromContext(ctx).Panic(
"Unable to fetch knative.dev/serving/pkg/client/informers/externalversions/serving/v1beta1.DomainMappingInformer from context.")
}
return untyped.(v1beta1.DomainMappingInformer)
}

View File

@ -0,0 +1,40 @@
/*
Copyright 2021 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"
controller "knative.dev/pkg/controller"
injection "knative.dev/pkg/injection"
fake "knative.dev/serving/pkg/client/injection/informers/factory/fake"
domainmapping "knative.dev/serving/pkg/client/injection/informers/serving/v1beta1/domainmapping"
)
var Get = domainmapping.Get
func init() {
injection.Fake.RegisterInformer(withInformer)
}
func withInformer(ctx context.Context) (context.Context, controller.Informer) {
f := fake.Get(ctx)
inf := f.Serving().V1beta1().DomainMappings()
return context.WithValue(ctx, domainmapping.Key{}, inf), inf.Informer()
}

View File

@ -0,0 +1,65 @@
/*
Copyright 2021 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 filtered
import (
context "context"
controller "knative.dev/pkg/controller"
injection "knative.dev/pkg/injection"
logging "knative.dev/pkg/logging"
v1beta1 "knative.dev/serving/pkg/client/informers/externalversions/serving/v1beta1"
filtered "knative.dev/serving/pkg/client/injection/informers/factory/filtered"
)
func init() {
injection.Default.RegisterFilteredInformers(withInformer)
}
// Key is used for associating the Informer inside the context.Context.
type Key struct {
Selector string
}
func withInformer(ctx context.Context) (context.Context, []controller.Informer) {
untyped := ctx.Value(filtered.LabelKey{})
if untyped == nil {
logging.FromContext(ctx).Panic(
"Unable to fetch labelkey from context.")
}
labelSelectors := untyped.([]string)
infs := []controller.Informer{}
for _, selector := range labelSelectors {
f := filtered.Get(ctx, selector)
inf := f.Serving().V1beta1().DomainMappings()
ctx = context.WithValue(ctx, Key{Selector: selector}, inf)
infs = append(infs, inf.Informer())
}
return ctx, infs
}
// Get extracts the typed informer from the context.
func Get(ctx context.Context, selector string) v1beta1.DomainMappingInformer {
untyped := ctx.Value(Key{Selector: selector})
if untyped == nil {
logging.FromContext(ctx).Panicf(
"Unable to fetch knative.dev/serving/pkg/client/informers/externalversions/serving/v1beta1.DomainMappingInformer with selector %s from context.", selector)
}
return untyped.(v1beta1.DomainMappingInformer)
}

View File

@ -0,0 +1,52 @@
/*
Copyright 2021 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"
controller "knative.dev/pkg/controller"
injection "knative.dev/pkg/injection"
logging "knative.dev/pkg/logging"
factoryfiltered "knative.dev/serving/pkg/client/injection/informers/factory/filtered"
filtered "knative.dev/serving/pkg/client/injection/informers/serving/v1beta1/domainmapping/filtered"
)
var Get = filtered.Get
func init() {
injection.Fake.RegisterFilteredInformers(withInformer)
}
func withInformer(ctx context.Context) (context.Context, []controller.Informer) {
untyped := ctx.Value(factoryfiltered.LabelKey{})
if untyped == nil {
logging.FromContext(ctx).Panic(
"Unable to fetch labelkey from context.")
}
labelSelectors := untyped.([]string)
infs := []controller.Informer{}
for _, selector := range labelSelectors {
f := factoryfiltered.Get(ctx, selector)
inf := f.Serving().V1beta1().DomainMappings()
ctx = context.WithValue(ctx, filtered.Key{Selector: selector}, inf)
infs = append(infs, inf.Informer())
}
return ctx, infs
}

View File

@ -0,0 +1,162 @@
/*
Copyright 2021 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 domainmapping
import (
context "context"
fmt "fmt"
reflect "reflect"
strings "strings"
zap "go.uber.org/zap"
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"
kubeclient "knative.dev/pkg/client/injection/kube/client"
controller "knative.dev/pkg/controller"
logging "knative.dev/pkg/logging"
logkey "knative.dev/pkg/logging/logkey"
reconciler "knative.dev/pkg/reconciler"
versionedscheme "knative.dev/serving/pkg/client/clientset/versioned/scheme"
client "knative.dev/serving/pkg/client/injection/client"
domainmapping "knative.dev/serving/pkg/client/injection/informers/serving/v1beta1/domainmapping"
)
const (
defaultControllerAgentName = "domainmapping-controller"
defaultFinalizerName = "domainmappings.serving.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 by 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.Fatal("Up to one options function is supported, found: ", len(optionsFns))
}
domainmappingInformer := domainmapping.Get(ctx)
lister := domainmappingInformer.Lister()
var promoteFilterFunc func(obj interface{}) bool
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 {
if promoteFilterFunc != nil {
if ok := promoteFilterFunc(elt); !ok {
continue
}
}
enq(bkt, types.NamespacedName{
Namespace: elt.GetNamespace(),
Name: elt.GetName(),
})
}
return nil
},
},
Client: client.Get(ctx),
Lister: lister,
reconciler: r,
finalizerName: defaultFinalizerName,
}
ctrType := reflect.TypeOf(r).Elem()
ctrTypeName := fmt.Sprintf("%s.%s", ctrType.PkgPath(), ctrType.Name())
ctrTypeName = strings.ReplaceAll(ctrTypeName, "/", ".")
logger = logger.With(
zap.String(logkey.ControllerType, ctrTypeName),
zap.String(logkey.Kind, "serving.knative.dev.DomainMapping"),
)
impl := controller.NewImpl(rec, logger, ctrTypeName)
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
}
if opts.SkipStatusUpdates {
rec.skipStatusUpdates = true
}
if opts.DemoteFunc != nil {
rec.DemoteFunc = opts.DemoteFunc
}
if opts.PromoteFilterFunc != nil {
promoteFilterFunc = opts.PromoteFilterFunc
}
}
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)
}

View File

@ -0,0 +1,455 @@
/*
Copyright 2021 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 domainmapping
import (
context "context"
json "encoding/json"
fmt "fmt"
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"
record "k8s.io/client-go/tools/record"
controller "knative.dev/pkg/controller"
kmp "knative.dev/pkg/kmp"
logging "knative.dev/pkg/logging"
reconciler "knative.dev/pkg/reconciler"
v1beta1 "knative.dev/serving/pkg/apis/serving/v1beta1"
versioned "knative.dev/serving/pkg/client/clientset/versioned"
servingv1beta1 "knative.dev/serving/pkg/client/listers/serving/v1beta1"
)
// Interface defines the strongly typed interfaces to be implemented by a
// controller reconciling v1beta1.DomainMapping.
type Interface interface {
// ReconcileKind implements custom logic to reconcile v1beta1.DomainMapping. 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 *v1beta1.DomainMapping) reconciler.Event
}
// Finalizer defines the strongly typed interfaces to be implemented by a
// controller finalizing v1beta1.DomainMapping.
type Finalizer interface {
// FinalizeKind implements custom logic to finalize v1beta1.DomainMapping. 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 *v1beta1.DomainMapping) reconciler.Event
}
// ReadOnlyInterface defines the strongly typed interfaces to be implemented by a
// controller reconciling v1beta1.DomainMapping if they want to process resources for which
// they are not the leader.
type ReadOnlyInterface interface {
// ObserveKind implements logic to observe v1beta1.DomainMapping.
// This method should not write to the API.
ObserveKind(ctx context.Context, o *v1beta1.DomainMapping) reconciler.Event
}
// ReadOnlyFinalizer defines the strongly typed interfaces to be implemented by a
// controller finalizing v1beta1.DomainMapping 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 v1beta1.DomainMapping.
// This method should not write to the API.
ObserveFinalizeKind(ctx context.Context, o *v1beta1.DomainMapping) reconciler.Event
}
type doReconcile func(ctx context.Context, o *v1beta1.DomainMapping) reconciler.Event
// reconcilerImpl implements controller.Reconciler for v1beta1.DomainMapping 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 servingv1beta1.DomainMappingLister
// 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
// skipStatusUpdates configures whether or not this reconciler automatically updates
// the status of the reconciled resource.
skipStatusUpdates bool
}
// 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 servingv1beta1.DomainMappingLister, 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.Fatal("Up to one options struct is supported, found: ", 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
}
if opts.SkipStatusUpdates {
rec.skipStatusUpdates = true
}
if opts.DemoteFunc != nil {
rec.DemoteFunc = opts.DemoteFunc
}
}
return rec
}
// Reconcile implements controller.Reconciler
func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
logger := logging.FromContext(ctx)
// Initialize the reconciler state. This will convert the namespace/name
// string into a distinct namespace and name, determine if this instance of
// the reconciler is the leader, and any additional interfaces implemented
// by the reconciler. Returns an error is the resource key is invalid.
s, err := newState(key, r)
if err != nil {
logger.Error("Invalid resource key: ", key)
return nil
}
// If we are not the leader, and we don't implement either ReadOnly
// observer interfaces, then take a fast-path out.
if s.isNotLeaderNorObserver() {
return controller.NewSkipKey(key)
}
// 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.DomainMappings(s.namespace)
original, err := getter.Get(s.name)
if errors.IsNotFound(err) {
// The resource may no longer exist, in which case we stop processing and call
// the ObserveDeletion handler if appropriate.
logger.Debugf("Resource %q no longer exists", key)
if del, ok := r.reconciler.(reconciler.OnDeletionInterface); ok {
return del.ObserveDeletion(ctx, types.NamespacedName{
Namespace: s.namespace,
Name: s.name,
})
}
return nil
} else if err != nil {
return err
}
// Don't modify the informers copy.
resource := original.DeepCopy()
var reconcileEvent reconciler.Event
name, do := s.reconcileMethodFor(resource)
// Append the target method to the logger.
logger = logger.With(zap.String("targetMethod", name))
switch name {
case reconciler.DoReconcileKind:
// 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)
}
if !r.skipStatusUpdates {
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 = do(ctx, resource)
if !r.skipStatusUpdates {
reconciler.PostProcessReconcile(ctx, resource, original)
}
case reconciler.DoFinalizeKind:
// For finalizing reconcilers, if this resource being marked for deletion
// and reconciled cleanly (nil or normal event), remove the finalizer.
reconcileEvent = do(ctx, resource)
if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil {
return fmt.Errorf("failed to clear finalizers: %w", err)
}
case reconciler.DoObserveKind, reconciler.DoObserveFinalizeKind:
// Observe any changes to this resource, since we are not the leader.
reconcileEvent = do(ctx, resource)
}
// Synchronize the status.
switch {
case r.skipStatusUpdates:
// This reconciler implementation is configured to skip resource updates.
// This may mean this reconciler does not observe spec, but reconciles external changes.
case 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.
case !s.isLeader:
// High-availability reconcilers may have many replicas watching the resource, but only
// the elected leader is expected to write modifications.
logger.Warn("Saw status changes when we aren't the leader!")
default:
if err = r.updateStatus(ctx, 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.Event(resource, event.EventType, event.Reason, event.Error())
// 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(ctx context.Context, existing *v1beta1.DomainMapping, desired *v1beta1.DomainMapping) 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.ServingV1beta1().DomainMappings(desired.Namespace)
existing, err = getter.Get(ctx, desired.Name, metav1.GetOptions{})
if err != nil {
return err
}
}
// If there's nothing to update, just return.
if equality.Semantic.DeepEqual(existing.Status, desired.Status) {
return nil
}
if diff, err := kmp.SafeDiff(existing.Status, desired.Status); err == nil && diff != "" {
logging.FromContext(ctx).Debug("Updating status with: ", diff)
}
existing.Status = desired.Status
updater := r.Client.ServingV1beta1().DomainMappings(existing.Namespace)
_, err = updater.UpdateStatus(ctx, existing, metav1.UpdateOptions{})
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 *v1beta1.DomainMapping) (*v1beta1.DomainMapping, error) {
getter := r.Lister.DomainMappings(resource.Namespace)
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.ServingV1beta1().DomainMappings(resource.Namespace)
resourceName := resource.Name
updated, err := patcher.Patch(ctx, resourceName, types.MergePatchType, patch, metav1.PatchOptions{})
if err != nil {
r.Recorder.Eventf(existing, v1.EventTypeWarning, "FinalizerUpdateFailed",
"Failed to update finalizers for %q: %v", resourceName, err)
} else {
r.Recorder.Eventf(updated, v1.EventTypeNormal, "FinalizerUpdate",
"Updated %q finalizers", resource.GetName())
}
return updated, err
}
func (r *reconcilerImpl) setFinalizerIfFinalizer(ctx context.Context, resource *v1beta1.DomainMapping) (*v1beta1.DomainMapping, 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 *v1beta1.DomainMapping, reconcileEvent reconciler.Event) (*v1beta1.DomainMapping, 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)
}

View File

@ -0,0 +1,106 @@
/*
Copyright 2021 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 domainmapping
import (
fmt "fmt"
types "k8s.io/apimachinery/pkg/types"
cache "k8s.io/client-go/tools/cache"
reconciler "knative.dev/pkg/reconciler"
v1beta1 "knative.dev/serving/pkg/apis/serving/v1beta1"
)
// state is used to track the state of a reconciler in a single run.
type state struct {
// key is the original reconciliation key from the queue.
key string
// namespace is the namespace split from the reconciliation key.
namespace string
// name is the name split from the reconciliation key.
name string
// reconciler is the reconciler.
reconciler Interface
// roi is the read only interface cast of the reconciler.
roi ReadOnlyInterface
// isROI (Read Only Interface) the reconciler only observes reconciliation.
isROI bool
// rof is the read only finalizer cast of the reconciler.
rof ReadOnlyFinalizer
// isROF (Read Only Finalizer) the reconciler only observes finalize.
isROF bool
// isLeader the instance of the reconciler is the elected leader.
isLeader bool
}
func newState(key string, r *reconcilerImpl) (*state, error) {
// Convert the namespace/name string into a distinct namespace and name.
namespace, name, err := cache.SplitMetaNamespaceKey(key)
if err != nil {
return nil, fmt.Errorf("invalid resource key: %s", key)
}
roi, isROI := r.reconciler.(ReadOnlyInterface)
rof, isROF := r.reconciler.(ReadOnlyFinalizer)
isLeader := r.IsLeaderFor(types.NamespacedName{
Namespace: namespace,
Name: name,
})
return &state{
key: key,
namespace: namespace,
name: name,
reconciler: r.reconciler,
roi: roi,
isROI: isROI,
rof: rof,
isROF: isROF,
isLeader: isLeader,
}, nil
}
// isNotLeaderNorObserver checks to see if this reconciler with the current
// state is enabled to do any work or not.
// isNotLeaderNorObserver returns true when there is no work possible for the
// reconciler.
func (s *state) isNotLeaderNorObserver() bool {
if !s.isLeader && !s.isROI && !s.isROF {
// If we are not the leader, and we don't implement either ReadOnly
// interface, then take a fast-path out.
return true
}
return false
}
func (s *state) reconcileMethodFor(o *v1beta1.DomainMapping) (string, doReconcile) {
if o.GetDeletionTimestamp().IsZero() {
if s.isLeader {
return reconciler.DoReconcileKind, s.reconciler.ReconcileKind
} else if s.isROI {
return reconciler.DoObserveKind, s.roi.ObserveKind
}
} else if fin, ok := s.reconciler.(Finalizer); s.isLeader && ok {
return reconciler.DoFinalizeKind, fin.FinalizeKind
} else if !s.isLeader && s.isROF {
return reconciler.DoObserveFinalizeKind, s.rof.ObserveFinalizeKind
}
return "unknown", nil
}

View File

@ -0,0 +1,99 @@
/*
Copyright 2021 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 v1beta1
import (
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
v1beta1 "knative.dev/serving/pkg/apis/serving/v1beta1"
)
// DomainMappingLister helps list DomainMappings.
// All objects returned here must be treated as read-only.
type DomainMappingLister interface {
// List lists all DomainMappings in the indexer.
// Objects returned here must be treated as read-only.
List(selector labels.Selector) (ret []*v1beta1.DomainMapping, err error)
// DomainMappings returns an object that can list and get DomainMappings.
DomainMappings(namespace string) DomainMappingNamespaceLister
DomainMappingListerExpansion
}
// domainMappingLister implements the DomainMappingLister interface.
type domainMappingLister struct {
indexer cache.Indexer
}
// NewDomainMappingLister returns a new DomainMappingLister.
func NewDomainMappingLister(indexer cache.Indexer) DomainMappingLister {
return &domainMappingLister{indexer: indexer}
}
// List lists all DomainMappings in the indexer.
func (s *domainMappingLister) List(selector labels.Selector) (ret []*v1beta1.DomainMapping, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v1beta1.DomainMapping))
})
return ret, err
}
// DomainMappings returns an object that can list and get DomainMappings.
func (s *domainMappingLister) DomainMappings(namespace string) DomainMappingNamespaceLister {
return domainMappingNamespaceLister{indexer: s.indexer, namespace: namespace}
}
// DomainMappingNamespaceLister helps list and get DomainMappings.
// All objects returned here must be treated as read-only.
type DomainMappingNamespaceLister interface {
// List lists all DomainMappings in the indexer for a given namespace.
// Objects returned here must be treated as read-only.
List(selector labels.Selector) (ret []*v1beta1.DomainMapping, err error)
// Get retrieves the DomainMapping from the indexer for a given namespace and name.
// Objects returned here must be treated as read-only.
Get(name string) (*v1beta1.DomainMapping, error)
DomainMappingNamespaceListerExpansion
}
// domainMappingNamespaceLister implements the DomainMappingNamespaceLister
// interface.
type domainMappingNamespaceLister struct {
indexer cache.Indexer
namespace string
}
// List lists all DomainMappings in the indexer for a given namespace.
func (s domainMappingNamespaceLister) List(selector labels.Selector) (ret []*v1beta1.DomainMapping, err error) {
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*v1beta1.DomainMapping))
})
return ret, err
}
// Get retrieves the DomainMapping from the indexer for a given namespace and name.
func (s domainMappingNamespaceLister) Get(name string) (*v1beta1.DomainMapping, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v1beta1.Resource("domainmapping"), name)
}
return obj.(*v1beta1.DomainMapping), nil
}

View File

@ -0,0 +1,27 @@
/*
Copyright 2021 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 v1beta1
// DomainMappingListerExpansion allows custom methods to be added to
// DomainMappingLister.
type DomainMappingListerExpansion interface{}
// DomainMappingNamespaceListerExpansion allows custom methods to be added to
// DomainMappingNamespaceLister.
type DomainMappingNamespaceListerExpansion interface{}

View File

@ -34,6 +34,7 @@ import (
"knative.dev/serving/pkg/client/clientset/versioned"
servingv1 "knative.dev/serving/pkg/client/clientset/versioned/typed/serving/v1"
servingv1alpha1 "knative.dev/serving/pkg/client/clientset/versioned/typed/serving/v1alpha1"
servingv1beta1 "knative.dev/serving/pkg/client/clientset/versioned/typed/serving/v1beta1"
// Every E2E test requires this, so add it here.
_ "knative.dev/pkg/metrics/testing"
@ -43,6 +44,7 @@ import (
type Clients struct {
KubeClient kubernetes.Interface
ServingAlphaClient *ServingAlphaClients
ServingBetaClient *ServingBetaClients
ServingClient *ServingClients
NetworkingClient *NetworkingClients
Dynamic dynamic.Interface
@ -53,6 +55,11 @@ type ServingAlphaClients struct {
DomainMappings servingv1alpha1.DomainMappingInterface
}
// ServingBetaClients holds instances of interfaces for making requests to knative serving clients.
type ServingBetaClients struct {
DomainMappings servingv1beta1.DomainMappingInterface
}
// ServingClients holds instances of interfaces for making requests to knative serving clients.
type ServingClients struct {
Routes servingv1.RouteInterface
@ -96,6 +103,11 @@ func NewClients(cfg *rest.Config, namespace string) (*Clients, error) {
return nil, err
}
clients.ServingBetaClient, err = newServingBetaClients(cfg, namespace)
if err != nil {
return nil, err
}
clients.Dynamic, err = dynamic.NewForConfig(cfg)
if err != nil {
return nil, err
@ -136,6 +148,19 @@ func newServingAlphaClients(cfg *rest.Config, namespace string) (*ServingAlphaCl
}, nil
}
// newServingBetaClients instantiates and returns the serving clientset required to make requests to the
// knative serving cluster.
func newServingBetaClients(cfg *rest.Config, namespace string) (*ServingBetaClients, error) {
cs, err := versioned.NewForConfig(cfg)
if err != nil {
return nil, err
}
return &ServingBetaClients{
DomainMappings: cs.ServingV1beta1().DomainMappings(namespace),
}, nil
}
// newServingClients instantiates and returns the serving clientset required to make requests to the
// knative serving cluster.
func newServingClients(cfg *rest.Config, namespace string) (*ServingClients, error) {

View File

@ -0,0 +1,222 @@
// +build e2e
/*
Copyright 2021 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.
*/
package v1alpha1
import (
"context"
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
duckv1 "knative.dev/pkg/apis/duck/v1"
"knative.dev/pkg/reconciler"
pkgtest "knative.dev/pkg/test"
"knative.dev/pkg/test/spoof"
"knative.dev/serving/pkg/apis/serving/v1beta1"
"knative.dev/serving/test"
"knative.dev/serving/test/e2e"
v1test "knative.dev/serving/test/v1"
)
func TestDomainMapping(t *testing.T) {
if !test.ServingFlags.EnableBetaFeatures {
t.Skip("Beta features not enabled")
}
t.Parallel()
ctx, clients := context.Background(), test.Setup(t)
names := test.ResourceNames{
Service: test.ObjectNameForTest(t),
Image: test.PizzaPlanet1,
}
// Clean up on test failure or interrupt.
test.EnsureTearDown(t, clients, &names)
// Set up initial Service.
svc, err := v1test.CreateServiceReady(t, clients, &names)
if err != nil {
t.Fatalf("Failed to create initial Service %v: %v", names.Service, err)
}
// Using fixed hostnames can lead to conflicts when multiple tests run at
// once, so include the svc name to avoid collisions.
host := svc.Service.Name + ".example.org"
// Set resolvabledomain for custom domain to false by default.
resolvableCustomDomain := false
if test.ServingFlags.CustomDomain != "" {
host = svc.Service.Name + "." + test.ServingFlags.CustomDomain
resolvableCustomDomain = true
}
// Point DomainMapping at our service.
var dm *v1beta1.DomainMapping
if err := reconciler.RetryTestErrors(func(int) error {
dm, err = clients.ServingBetaClient.DomainMappings.Create(ctx, &v1beta1.DomainMapping{
ObjectMeta: metav1.ObjectMeta{
Name: host,
Namespace: svc.Service.Namespace,
},
Spec: v1beta1.DomainMappingSpec{
Ref: duckv1.KReference{
Namespace: svc.Service.Namespace,
Name: svc.Service.Name,
APIVersion: "serving.knative.dev/v1",
Kind: "Service",
},
},
}, metav1.CreateOptions{})
return err
}); err != nil {
t.Fatalf("Create(DomainMapping) = %v, expected no error", err)
}
t.Cleanup(func() {
clients.ServingAlphaClient.DomainMappings.Delete(ctx, dm.Name, metav1.DeleteOptions{})
})
// Wait for DomainMapping to go Ready.
waitErr := wait.PollImmediate(test.PollInterval, test.PollTimeout, func() (bool, error) {
var err error
dm, err = clients.ServingBetaClient.DomainMappings.Get(context.Background(), dm.Name, metav1.GetOptions{})
if err != nil {
return true, err
}
return dm.IsReady(), nil
})
if waitErr != nil {
t.Fatalf("The DomainMapping %s was not marked as Ready: %v", dm.Name, waitErr)
}
endpoint := dm.Status.URL.URL()
t.Log("Probing", endpoint)
if _, err := pkgtest.WaitForEndpointState(
context.Background(),
clients.KubeClient,
t.Logf,
endpoint,
v1test.RetryingRouteInconsistency(spoof.MatchesAllOf(spoof.IsStatusOK, spoof.MatchesBody(test.PizzaPlanetText1))),
"WaitForSuccessfulResponse",
resolvableCustomDomain,
test.AddRootCAtoTransport(context.Background(), t.Logf, clients, test.ServingFlags.HTTPS)); err != nil {
t.Fatalf("Error probing %s: %v", endpoint, err)
}
altClients := e2e.SetupAlternativeNamespace(t)
altNames := test.ResourceNames{
Service: test.ObjectNameForTest(t),
Image: test.PizzaPlanet2,
}
test.EnsureTearDown(t, altClients, &altNames)
// Set up second Service in alt namespace.
altSvc, err := v1test.CreateServiceReady(t, altClients, &altNames)
if err != nil {
t.Fatalf("Failed to create initial Service %v: %v", altNames.Service, err)
}
// Create second domain mapping with same name in alt namespace - this will collide with the existing mapping.
var altDm *v1beta1.DomainMapping
if err := reconciler.RetryTestErrors(func(int) error {
altDm, err = altClients.ServingBetaClient.DomainMappings.Create(ctx, &v1beta1.DomainMapping{
ObjectMeta: metav1.ObjectMeta{
Name: host,
Namespace: altSvc.Service.Namespace,
},
Spec: v1beta1.DomainMappingSpec{
Ref: duckv1.KReference{
Namespace: altSvc.Service.Namespace,
Name: altSvc.Service.Name,
APIVersion: "serving.knative.dev/v1",
Kind: "Service",
},
},
}, metav1.CreateOptions{})
return err
}); err != nil {
t.Fatalf("Create(DomainMapping) = %v, expected no error", err)
}
t.Cleanup(func() {
altClients.ServingAlphaClient.DomainMappings.Delete(ctx, altDm.Name, metav1.DeleteOptions{})
})
// Second domain mapping should go to DomainMappingConditionDomainClaimed=false state.
waitErr = wait.PollImmediate(test.PollInterval, test.PollTimeout, func() (bool, error) {
state, err := altClients.ServingAlphaClient.DomainMappings.Get(context.Background(), dm.Name, metav1.GetOptions{})
if err != nil {
return true, err
}
return state.Generation == state.Status.ObservedGeneration &&
state.Status.GetCondition(v1beta1.DomainMappingConditionDomainClaimed).IsFalse(), nil
})
if waitErr != nil {
t.Fatalf("The second DomainMapping %s did not enter DomainMappingConditionDomainClaimed=false state: %v", altDm.Name, waitErr)
}
// Because the second DomainMapping collided with the first, it should not have taken effect.
t.Log("Probing", endpoint)
if _, err := pkgtest.WaitForEndpointState(
context.Background(),
clients.KubeClient,
t.Logf,
endpoint,
v1test.RetryingRouteInconsistency(spoof.MatchesAllOf(spoof.IsStatusOK, spoof.MatchesBody(test.PizzaPlanetText1))),
"WaitForSuccessfulResponse",
resolvableCustomDomain,
test.AddRootCAtoTransport(context.Background(), t.Logf, clients, test.ServingFlags.HTTPS)); err != nil {
t.Fatalf("Error probing %s: %v", endpoint, err)
}
// Delete the first DomainMapping.
if err := clients.ServingAlphaClient.DomainMappings.Delete(ctx, dm.Name, metav1.DeleteOptions{}); err != nil {
t.Fatalf("Delete=%v, expected no error", err)
}
// The second DomainMapping should now be able to claim the domain.
waitErr = wait.PollImmediate(test.PollInterval, test.PollTimeout, func() (bool, error) {
var err error
altDm, err = altClients.ServingBetaClient.DomainMappings.Get(context.Background(), altDm.Name, metav1.GetOptions{})
if err != nil {
return true, err
}
return altDm.IsReady(), nil
})
if waitErr != nil {
t.Fatalf("The second DomainMapping %s was not marked as Ready: %v", dm.Name, waitErr)
}
endpoint = altDm.Status.URL.URL()
t.Log("Probing", endpoint)
if _, err := pkgtest.WaitForEndpointState(
context.Background(),
clients.KubeClient,
t.Logf,
endpoint,
v1test.RetryingRouteInconsistency(spoof.MatchesAllOf(spoof.IsStatusOK, spoof.MatchesBody(test.PizzaPlanetText2))),
"WaitForSuccessfulResponse",
resolvableCustomDomain,
test.AddRootCAtoTransport(context.Background(), t.Logf, clients, test.ServingFlags.HTTPS)); err != nil {
t.Fatalf("Error probing %s: %v", endpoint, err)
}
}