Skip to content

Commit fa0a9a5

Browse files
authored
RootSyncRollout controller (#3542)
1 parent 8ecda33 commit fa0a9a5

File tree

13 files changed

+1104
-5
lines changed

13 files changed

+1104
-5
lines changed

porch/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ TEST_GIT_SERVER_IMAGE ?= test-git-server
4949
# Only enable a subset of reconcilers in porch controllers by default. Use the RECONCILERS
5050
# env variable to specify a specific list of reconcilers or use
5151
# RECONCILERS=* to enable all known reconcilers.
52-
ALL_RECONCILERS="rootsyncsets,remoterootsyncsets,workloadidentitybindings,rootsyncdeployments,functiondiscovery,downstreampackages"
52+
ALL_RECONCILERS="rootsyncsets,remoterootsyncsets,workloadidentitybindings,rootsyncdeployments,functiondiscovery,downstreampackages,rootsyncrollouts"
5353
ifndef RECONCILERS
5454
ENABLED_RECONCILERS="rootsyncsets,remoterootsyncsets,workloadidentitybindings,functiondiscovery"
5555
else

porch/controllers/config/crd/bases/config.porch.kpt.dev_rootsyncdeployments.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,9 @@ spec:
200200
- type
201201
type: object
202202
type: array
203+
observedGeneration:
204+
format: int64
205+
type: integer
203206
type: object
204207
type: object
205208
served: true
Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
# Copyright 2022 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
---
16+
apiVersion: apiextensions.k8s.io/v1
17+
kind: CustomResourceDefinition
18+
metadata:
19+
annotations:
20+
controller-gen.kubebuilder.io/version: v0.8.0
21+
creationTimestamp: null
22+
name: rootsyncrollouts.config.porch.kpt.dev
23+
spec:
24+
group: config.porch.kpt.dev
25+
names:
26+
kind: RootSyncRollout
27+
listKind: RootSyncRolloutList
28+
plural: rootsyncrollouts
29+
singular: rootsyncrollout
30+
scope: Namespaced
31+
versions:
32+
- name: v1alpha1
33+
schema:
34+
openAPIV3Schema:
35+
description: RootSyncRollout is the Schema for the rootsyncrollouts API
36+
properties:
37+
apiVersion:
38+
description: 'APIVersion defines the versioned schema of this representation
39+
of an object. Servers should convert recognized schemas to the latest
40+
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
41+
type: string
42+
kind:
43+
description: 'Kind is a string value representing the REST resource this
44+
object represents. Servers may infer this from the endpoint the client
45+
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
46+
type: string
47+
metadata:
48+
type: object
49+
spec:
50+
description: RootSyncRolloutSpec
51+
properties:
52+
packages:
53+
properties:
54+
namespace:
55+
type: string
56+
selector:
57+
description: A label selector is a label query over a set of resources.
58+
The result of matchLabels and matchExpressions are ANDed. An
59+
empty label selector matches all objects. A null label selector
60+
matches no objects.
61+
properties:
62+
matchExpressions:
63+
description: matchExpressions is a list of label selector
64+
requirements. The requirements are ANDed.
65+
items:
66+
description: A label selector requirement is a selector
67+
that contains values, a key, and an operator that relates
68+
the key and values.
69+
properties:
70+
key:
71+
description: key is the label key that the selector
72+
applies to.
73+
type: string
74+
operator:
75+
description: operator represents a key's relationship
76+
to a set of values. Valid operators are In, NotIn,
77+
Exists and DoesNotExist.
78+
type: string
79+
values:
80+
description: values is an array of string values. If
81+
the operator is In or NotIn, the values array must
82+
be non-empty. If the operator is Exists or DoesNotExist,
83+
the values array must be empty. This array is replaced
84+
during a strategic merge patch.
85+
items:
86+
type: string
87+
type: array
88+
required:
89+
- key
90+
- operator
91+
type: object
92+
type: array
93+
matchLabels:
94+
additionalProperties:
95+
type: string
96+
description: matchLabels is a map of {key,value} pairs. A
97+
single {key,value} in the matchLabels map is equivalent
98+
to an element of matchExpressions, whose key field is "key",
99+
the operator is "In", and the values array contains only
100+
"value". The requirements are ANDed.
101+
type: object
102+
type: object
103+
type: object
104+
targets:
105+
properties:
106+
selector:
107+
description: A label selector is a label query over a set of resources.
108+
The result of matchLabels and matchExpressions are ANDed. An
109+
empty label selector matches all objects. A null label selector
110+
matches no objects.
111+
properties:
112+
matchExpressions:
113+
description: matchExpressions is a list of label selector
114+
requirements. The requirements are ANDed.
115+
items:
116+
description: A label selector requirement is a selector
117+
that contains values, a key, and an operator that relates
118+
the key and values.
119+
properties:
120+
key:
121+
description: key is the label key that the selector
122+
applies to.
123+
type: string
124+
operator:
125+
description: operator represents a key's relationship
126+
to a set of values. Valid operators are In, NotIn,
127+
Exists and DoesNotExist.
128+
type: string
129+
values:
130+
description: values is an array of string values. If
131+
the operator is In or NotIn, the values array must
132+
be non-empty. If the operator is Exists or DoesNotExist,
133+
the values array must be empty. This array is replaced
134+
during a strategic merge patch.
135+
items:
136+
type: string
137+
type: array
138+
required:
139+
- key
140+
- operator
141+
type: object
142+
type: array
143+
matchLabels:
144+
additionalProperties:
145+
type: string
146+
description: matchLabels is a map of {key,value} pairs. A
147+
single {key,value} in the matchLabels map is equivalent
148+
to an element of matchExpressions, whose key field is "key",
149+
the operator is "In", and the values array contains only
150+
"value". The requirements are ANDed.
151+
type: object
152+
type: object
153+
type: object
154+
type: object
155+
status:
156+
description: RootSyncRolloutStatus defines the observed state of RootSyncRollout
157+
properties:
158+
conditions:
159+
description: Conditions describes the reconciliation state of the
160+
object.
161+
items:
162+
description: "Condition contains details for one aspect of the current
163+
state of this API Resource. --- This struct is intended for direct
164+
use as an array at the field path .status.conditions. For example,
165+
type FooStatus struct{ // Represents the observations of a foo's
166+
current state. // Known .status.conditions.type are: \"Available\",
167+
\"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge
168+
// +listType=map // +listMapKey=type Conditions []metav1.Condition
169+
`json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
170+
protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }"
171+
properties:
172+
lastTransitionTime:
173+
description: lastTransitionTime is the last time the condition
174+
transitioned from one status to another. This should be when
175+
the underlying condition changed. If that is not known, then
176+
using the time when the API field changed is acceptable.
177+
format: date-time
178+
type: string
179+
message:
180+
description: message is a human readable message indicating
181+
details about the transition. This may be an empty string.
182+
maxLength: 32768
183+
type: string
184+
observedGeneration:
185+
description: observedGeneration represents the .metadata.generation
186+
that the condition was set based upon. For instance, if .metadata.generation
187+
is currently 12, but the .status.conditions[x].observedGeneration
188+
is 9, the condition is out of date with respect to the current
189+
state of the instance.
190+
format: int64
191+
minimum: 0
192+
type: integer
193+
reason:
194+
description: reason contains a programmatic identifier indicating
195+
the reason for the condition's last transition. Producers
196+
of specific condition types may define expected values and
197+
meanings for this field, and whether the values are considered
198+
a guaranteed API. The value should be a CamelCase string.
199+
This field may not be empty.
200+
maxLength: 1024
201+
minLength: 1
202+
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
203+
type: string
204+
status:
205+
description: status of the condition, one of True, False, Unknown.
206+
enum:
207+
- "True"
208+
- "False"
209+
- Unknown
210+
type: string
211+
type:
212+
description: type of condition in CamelCase or in foo.example.com/CamelCase.
213+
--- Many .condition.type values are consistent across resources
214+
like Available, but because arbitrary conditions can be useful
215+
(see .node.status.conditions), the ability to deconflict is
216+
important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
217+
maxLength: 316
218+
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
219+
type: string
220+
required:
221+
- lastTransitionTime
222+
- message
223+
- reason
224+
- status
225+
- type
226+
type: object
227+
type: array
228+
packageStatus:
229+
items:
230+
properties:
231+
package:
232+
type: string
233+
revision:
234+
items:
235+
properties:
236+
count:
237+
type: integer
238+
revision:
239+
type: string
240+
syncedCount:
241+
type: integer
242+
required:
243+
- count
244+
- revision
245+
- syncedCount
246+
type: object
247+
type: array
248+
required:
249+
- package
250+
- revision
251+
type: object
252+
type: array
253+
type: object
254+
type: object
255+
served: true
256+
storage: true
257+
subresources:
258+
status: {}
259+
status:
260+
acceptedNames:
261+
kind: ""
262+
plural: ""
263+
conditions: []
264+
storedVersions: []

porch/controllers/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import (
4444
"github.com/GoogleContainerTools/kpt/porch/controllers/klippy/pkg/controllers/klippy"
4545
"github.com/GoogleContainerTools/kpt/porch/controllers/remoterootsyncsets/pkg/controllers/remoterootsyncset"
4646
"github.com/GoogleContainerTools/kpt/porch/controllers/rootsyncdeployments/pkg/controllers/rootsyncdeployment"
47+
"github.com/GoogleContainerTools/kpt/porch/controllers/rootsyncrollouts/pkg/controllers/rootsyncrollout"
4748
"github.com/GoogleContainerTools/kpt/porch/controllers/rootsyncsets/pkg/controllers/rootsyncset"
4849
"github.com/GoogleContainerTools/kpt/porch/controllers/workloadidentitybindings/pkg/controllers/workloadidentitybinding"
4950
"github.com/GoogleContainerTools/kpt/porch/pkg/controllerrestmapper"
@@ -59,6 +60,7 @@ var (
5960
"klippy": &klippy.KlippyReconciler{},
6061
"rootsyncdeployments": rootsyncdeployment.NewRootSyncDeploymentReconciler(),
6162
"functiondiscovery": &functiondiscovery.FunctionReconciler{},
63+
"rootsyncrollouts": rootsyncrollout.NewRootSyncRolloutReconciler(),
6264
}
6365
)
6466

porch/controllers/rootsyncdeployments/api/v1alpha1/rootsyncdeployments_types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ type PackageRevisionRef struct {
3535

3636
// RootSyncDeploymentStatus defines the observed state of RootSyncDeployment
3737
type RootSyncDeploymentStatus struct {
38+
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
39+
3840
// Conditions describes the reconciliation state of the object.
3941
Conditions []metav1.Condition `json:"conditions,omitempty"`
4042

porch/controllers/rootsyncdeployments/pkg/controllers/rootsyncdeployment/controller.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,13 +157,14 @@ func (r *RootSyncDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.R
157157
return ctrl.Result{}, nil
158158
}
159159

160-
func (r *RootSyncDeploymentReconciler) updateStatus(ctx context.Context, rds *v1alpha1.RootSyncDeployment, clusterRefStatuses []v1alpha1.ClusterRefStatus) error {
161-
if equality.Semantic.DeepEqual(rds.Status.ClusterRefStatuses, clusterRefStatuses) {
160+
func (r *RootSyncDeploymentReconciler) updateStatus(ctx context.Context, rsd *v1alpha1.RootSyncDeployment, clusterRefStatuses []v1alpha1.ClusterRefStatus) error {
161+
if equality.Semantic.DeepEqual(rsd.Status.ClusterRefStatuses, clusterRefStatuses) {
162162
klog.Infof("Status has not changed, update not needed.")
163163
return nil
164164
}
165-
rds.Status.ClusterRefStatuses = clusterRefStatuses
166-
return r.Status().Update(ctx, rds)
165+
rsd.Status.ObservedGeneration = rsd.Generation
166+
rsd.Status.ClusterRefStatuses = clusterRefStatuses
167+
return r.Status().Update(ctx, rsd)
167168
}
168169

169170
func (r *RootSyncDeploymentReconciler) syncRootSyncDeployment(ctx context.Context, rsd *v1alpha1.RootSyncDeployment) ([]v1alpha1.ClusterRefStatus, error) {
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Package v1alpha1 contains API Schema definitions for the config.porch.kpt.dev v1alpha1 API group
16+
//+kubebuilder:object:generate=true
17+
//+groupName=config.porch.kpt.dev
18+
package v1alpha1
19+
20+
import (
21+
"k8s.io/apimachinery/pkg/runtime/schema"
22+
"sigs.k8s.io/controller-runtime/pkg/scheme"
23+
)
24+
25+
//go:generate go run sigs.k8s.io/controller-tools/cmd/[email protected] object object:headerFile="../../../../scripts/boilerplate.go.txt" paths="./..."
26+
27+
var (
28+
// GroupVersion is group version used to register these objects
29+
GroupVersion = schema.GroupVersion{Group: "config.porch.kpt.dev", Version: "v1alpha1"}
30+
31+
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
32+
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
33+
34+
// AddToScheme adds the types in this group-version to the given scheme.
35+
AddToScheme = SchemeBuilder.AddToScheme
36+
)

0 commit comments

Comments
 (0)