1/*
2Copyright 2016 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package apiservice
18
19import (
20	"context"
21	"fmt"
22
23	"k8s.io/apimachinery/pkg/fields"
24	"k8s.io/apimachinery/pkg/labels"
25	"k8s.io/apimachinery/pkg/runtime"
26	"k8s.io/apimachinery/pkg/util/validation/field"
27	"k8s.io/apiserver/pkg/registry/generic"
28	"k8s.io/apiserver/pkg/registry/rest"
29	"k8s.io/apiserver/pkg/storage"
30	"k8s.io/apiserver/pkg/storage/names"
31
32	"k8s.io/kube-aggregator/pkg/apis/apiregistration"
33	"k8s.io/kube-aggregator/pkg/apis/apiregistration/validation"
34	"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
35)
36
37type apiServerStrategy struct {
38	runtime.ObjectTyper
39	names.NameGenerator
40}
41
42// apiServerStrategy must implement rest.RESTCreateUpdateStrategy
43var _ rest.RESTCreateUpdateStrategy = apiServerStrategy{}
44var Strategy = apiServerStrategy{}
45
46// NewStrategy creates a new apiServerStrategy.
47func NewStrategy(typer runtime.ObjectTyper) rest.CreateUpdateResetFieldsStrategy {
48	return apiServerStrategy{typer, names.SimpleNameGenerator}
49}
50
51func (apiServerStrategy) NamespaceScoped() bool {
52	return false
53}
54
55func (apiServerStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
56	fields := map[fieldpath.APIVersion]*fieldpath.Set{
57		"apiregistration.k8s.io/v1": fieldpath.NewSet(
58			fieldpath.MakePathOrDie("status"),
59		),
60		"apiregistration.k8s.io/v1beta1": fieldpath.NewSet(
61			fieldpath.MakePathOrDie("status"),
62		),
63	}
64
65	return fields
66}
67
68func (apiServerStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
69	apiservice := obj.(*apiregistration.APIService)
70	apiservice.Status = apiregistration.APIServiceStatus{}
71
72	// mark local API services as immediately available on create
73	if apiservice.Spec.Service == nil {
74		apiregistration.SetAPIServiceCondition(apiservice, apiregistration.NewLocalAvailableAPIServiceCondition())
75	}
76}
77
78func (apiServerStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
79	newAPIService := obj.(*apiregistration.APIService)
80	oldAPIService := old.(*apiregistration.APIService)
81	newAPIService.Status = oldAPIService.Status
82}
83
84func (apiServerStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
85	return validation.ValidateAPIService(obj.(*apiregistration.APIService))
86}
87
88// WarningsOnCreate returns warnings for the creation of the given object.
89func (apiServerStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
90	return nil
91}
92
93func (apiServerStrategy) AllowCreateOnUpdate() bool {
94	return false
95}
96
97func (apiServerStrategy) AllowUnconditionalUpdate() bool {
98	return false
99}
100
101func (apiServerStrategy) Canonicalize(obj runtime.Object) {
102}
103
104func (apiServerStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
105	return validation.ValidateAPIServiceUpdate(obj.(*apiregistration.APIService), old.(*apiregistration.APIService))
106}
107
108// WarningsOnUpdate returns warnings for the given update.
109func (apiServerStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
110	return nil
111}
112
113type apiServerStatusStrategy struct {
114	runtime.ObjectTyper
115	names.NameGenerator
116}
117
118// NewStatusStrategy creates a new apiServerStatusStrategy.
119func NewStatusStrategy(typer runtime.ObjectTyper) rest.UpdateResetFieldsStrategy {
120	return apiServerStatusStrategy{typer, names.SimpleNameGenerator}
121}
122
123func (apiServerStatusStrategy) NamespaceScoped() bool {
124	return false
125}
126
127func (apiServerStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
128	fields := map[fieldpath.APIVersion]*fieldpath.Set{
129		"apiregistration.k8s.io/v1": fieldpath.NewSet(
130			fieldpath.MakePathOrDie("spec"),
131			fieldpath.MakePathOrDie("metadata"),
132		),
133		"apiregistration.k8s.io/v1beta1": fieldpath.NewSet(
134			fieldpath.MakePathOrDie("spec"),
135			fieldpath.MakePathOrDie("metadata"),
136		),
137	}
138
139	return fields
140}
141
142func (apiServerStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
143	newAPIService := obj.(*apiregistration.APIService)
144	oldAPIService := old.(*apiregistration.APIService)
145	newAPIService.Spec = oldAPIService.Spec
146	newAPIService.Labels = oldAPIService.Labels
147	newAPIService.Annotations = oldAPIService.Annotations
148	newAPIService.Finalizers = oldAPIService.Finalizers
149	newAPIService.OwnerReferences = oldAPIService.OwnerReferences
150}
151
152func (apiServerStatusStrategy) AllowCreateOnUpdate() bool {
153	return false
154}
155
156func (apiServerStatusStrategy) AllowUnconditionalUpdate() bool {
157	return false
158}
159
160// Canonicalize normalizes the object after validation.
161func (apiServerStatusStrategy) Canonicalize(obj runtime.Object) {
162}
163
164// ValidateUpdate validates an update of apiServerStatusStrategy.
165func (apiServerStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
166	return validation.ValidateAPIServiceStatusUpdate(obj.(*apiregistration.APIService), old.(*apiregistration.APIService))
167}
168
169// WarningsOnUpdate returns warnings for the given update.
170func (apiServerStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
171	return nil
172}
173
174// GetAttrs returns the labels and fields of an API server for filtering purposes.
175func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
176	apiserver, ok := obj.(*apiregistration.APIService)
177	if !ok {
178		return nil, nil, fmt.Errorf("given object is not a APIService")
179	}
180	return labels.Set(apiserver.ObjectMeta.Labels), ToSelectableFields(apiserver), nil
181}
182
183// MatchAPIService is the filter used by the generic etcd backend to watch events
184// from etcd to clients of the apiserver only interested in specific labels/fields.
185func MatchAPIService(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
186	return storage.SelectionPredicate{
187		Label:    label,
188		Field:    field,
189		GetAttrs: GetAttrs,
190	}
191}
192
193// ToSelectableFields returns a field set that represents the object.
194func ToSelectableFields(obj *apiregistration.APIService) fields.Set {
195	return generic.ObjectMetaFieldsSet(&obj.ObjectMeta, true)
196}
197