Skip to content
This repository was archived by the owner on Jun 13, 2023. It is now read-only.

Commit 0d1070d

Browse files
Declarative v2 (#194)
* feat: pointer of installinfo and postrun hooks * feat: declarative v2 * feat: ssa & crd uninstallation * feat: ssa & crd uninstallation * feat: ssa & crd uninstallation * feat: ssa & crd uninstallation * feat: ssa & crd uninstallation * feat: ssa & crd uninstallation * feat: csa trial * feat: concurrent cleanup * tests: write concurrently runnable tests and add custom ready check for mocking states and add manifest cache root * tests: write concurrently runnable tests and add custom ready check for mocking states and add manifest cache root * add deployment_test.go * chore: extract helm reconciliation * chore: fixup lint * tests: kustomize and raw, also settings * tests: kustomize and raw, also settings * tests: kustomize and raw, also settings * chore: refactoring * chore: refactoring * tests: add hook tests to prepare for kmm * tests: client abstraction skr/kcp * chore: cleanup * test: render and cache * test: ssa * feat: add reconciliation skip on label * chore: transform tests and warning for nonmap values in helm renderer * chore: pr-review and fix bad statesync * chore: pr-review * chore: split reconcile loop Co-authored-by: Xin Ruan <[email protected]>
1 parent 348f6d5 commit 0d1070d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+4607
-47
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
---
2+
apiVersion: apiextensions.k8s.io/v1
3+
kind: CustomResourceDefinition
4+
metadata:
5+
annotations:
6+
controller-gen.kubebuilder.io/version: v0.9.2
7+
creationTimestamp: null
8+
name: testapis.test.declarative.kyma-project.io
9+
spec:
10+
group: test.declarative.kyma-project.io
11+
names:
12+
kind: TestAPI
13+
listKind: TestAPIList
14+
plural: testapis
15+
singular: testapi
16+
scope: Namespaced
17+
versions:
18+
- name: v1
19+
schema:
20+
openAPIV3Schema:
21+
properties:
22+
apiVersion:
23+
description: 'APIVersion defines the versioned schema of this representation
24+
of an object. Servers should convert recognized schemas to the latest
25+
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
26+
type: string
27+
kind:
28+
description: 'Kind is a string value representing the REST resource this
29+
object represents. Servers may infer this from the endpoint the client
30+
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
31+
type: string
32+
metadata:
33+
type: object
34+
spec:
35+
description: TestAPISpec defines the desired state of TestAPI.
36+
properties:
37+
manifestName:
38+
description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
39+
Important: Run "make" to regenerate code after modifying this file'
40+
type: string
41+
type: object
42+
status:
43+
description: Status defines the observed state of CustomObject.
44+
properties:
45+
conditions:
46+
description: Conditions contain a set of conditionals to determine
47+
the State of Status. If all Conditions are met, State is expected
48+
to be in StateReady.
49+
items:
50+
description: "Condition contains details for one aspect of the current
51+
state of this API Resource. --- This struct is intended for direct
52+
use as an array at the field path .status.conditions. For example,
53+
\n type FooStatus struct{ // Represents the observations of a
54+
foo's current state. // Known .status.conditions.type are: \"Available\",
55+
\"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge
56+
// +listType=map // +listMapKey=type Conditions []metav1.Condition
57+
`json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
58+
protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }"
59+
properties:
60+
lastTransitionTime:
61+
description: lastTransitionTime is the last time the condition
62+
transitioned from one status to another. This should be when
63+
the underlying condition changed. If that is not known, then
64+
using the time when the API field changed is acceptable.
65+
format: date-time
66+
type: string
67+
message:
68+
description: message is a human readable message indicating
69+
details about the transition. This may be an empty string.
70+
maxLength: 32768
71+
type: string
72+
observedGeneration:
73+
description: observedGeneration represents the .metadata.generation
74+
that the condition was set based upon. For instance, if .metadata.generation
75+
is currently 12, but the .status.conditions[x].observedGeneration
76+
is 9, the condition is out of date with respect to the current
77+
state of the instance.
78+
format: int64
79+
minimum: 0
80+
type: integer
81+
reason:
82+
description: reason contains a programmatic identifier indicating
83+
the reason for the condition's last transition. Producers
84+
of specific condition types may define expected values and
85+
meanings for this field, and whether the values are considered
86+
a guaranteed API. The value should be a CamelCase string.
87+
This field may not be empty.
88+
maxLength: 1024
89+
minLength: 1
90+
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
91+
type: string
92+
status:
93+
description: status of the condition, one of True, False, Unknown.
94+
enum:
95+
- "True"
96+
- "False"
97+
- Unknown
98+
type: string
99+
type:
100+
description: type of condition in CamelCase or in foo.example.com/CamelCase.
101+
--- Many .condition.type values are consistent across resources
102+
like Available, but because arbitrary conditions can be useful
103+
(see .node.status.conditions), the ability to deconflict is
104+
important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
105+
maxLength: 316
106+
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])$
107+
type: string
108+
required:
109+
- lastTransitionTime
110+
- message
111+
- reason
112+
- status
113+
- type
114+
type: object
115+
type: array
116+
lastOperation:
117+
description: LastOperation defines the last operation from the control-loop.
118+
properties:
119+
lastUpdateTime:
120+
format: date-time
121+
type: string
122+
operation:
123+
type: string
124+
required:
125+
- operation
126+
type: object
127+
state:
128+
description: State signifies current state of CustomObject. Value
129+
can be one of ("Ready", "Processing", "Error", "Deleting").
130+
enum:
131+
- Processing
132+
- Deleting
133+
- Ready
134+
- Error
135+
type: string
136+
synced:
137+
description: Synced determine a list of Resources that are currently
138+
actively synced. All resources that are synced are considered for
139+
orphan removal on configuration changes, and it is used to determine
140+
effective differences from one state to the next.
141+
items:
142+
properties:
143+
group:
144+
type: string
145+
kind:
146+
type: string
147+
name:
148+
type: string
149+
namespace:
150+
type: string
151+
version:
152+
type: string
153+
required:
154+
- group
155+
- kind
156+
- name
157+
- namespace
158+
- version
159+
type: object
160+
type: array
161+
x-kubernetes-list-type: atomic
162+
required:
163+
- conditions
164+
- state
165+
- synced
166+
type: object
167+
type: object
168+
served: true
169+
storage: true
170+
subresources:
171+
status: {}

go.mod

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ require (
77
github.com/go-logr/zapr v1.2.3
88
github.com/go-logr/zerologr v1.2.2
99
github.com/gofrs/flock v0.8.1
10+
github.com/golang/mock v1.6.0
1011
github.com/google/go-containerregistry v0.10.0
1112
github.com/invopop/jsonschema v0.5.0
1213
github.com/kyma-project/runtime-watcher/listener v0.0.0-20221006112208-0dd54057307c
1314
github.com/onsi/ginkgo/v2 v2.5.0
1415
github.com/onsi/gomega v1.24.1
1516
github.com/pkg/errors v0.9.1
1617
github.com/rs/zerolog v1.28.0
17-
github.com/stretchr/testify v1.8.0
18+
github.com/stretchr/testify v1.8.1
1819
github.com/xeipuuv/gojsonschema v1.2.0
1920
go.uber.org/zap v1.23.0
2021
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9
@@ -130,6 +131,7 @@ require (
130131
github.com/spf13/cast v1.5.0 // indirect
131132
github.com/spf13/cobra v1.5.0 // indirect
132133
github.com/spf13/pflag v1.0.5 // indirect
134+
github.com/stretchr/objx v0.5.0 // indirect
133135
github.com/vbatts/tar-split v0.11.2 // indirect
134136
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
135137
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect

go.sum

+5
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
347347
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
348348
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
349349
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
350+
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
350351
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
351352
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
352353
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -883,6 +884,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
883884
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
884885
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
885886
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
887+
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
888+
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
886889
github.com/stretchr/testify v0.0.0-20170130113145-4d4bfba8f1d1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
887890
github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
888891
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@@ -895,6 +898,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
895898
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
896899
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
897900
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
901+
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
902+
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
898903
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
899904
github.com/sylvia7788/contextcheck v1.0.4/go.mod h1:vuPKJMQ7MQ91ZTqfdyreNKwZjyUg6KO+IebVyQDedZQ=
900905
github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM=

pkg/client/client_proxy.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ func (p *ProxyClient) Delete(ctx context.Context, obj client.Object, opts ...cli
7171
}
7272

7373
// DeleteAllOf implements client.Client.
74-
func (p *ProxyClient) DeleteAllOf(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption,
74+
func (p *ProxyClient) DeleteAllOf(
75+
ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption,
7576
) error {
7677
if _, err := getResourceMapping(obj, p.mapper, true); err != nil {
7778
return err
@@ -80,7 +81,8 @@ func (p *ProxyClient) DeleteAllOf(ctx context.Context, obj client.Object, opts .
8081
}
8182

8283
// Patch implements client.Client.
83-
func (p *ProxyClient) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption,
84+
func (p *ProxyClient) Patch(
85+
ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption,
8486
) error {
8587
if _, err := getResourceMapping(obj, p.mapper, true); err != nil {
8688
return err

pkg/client/factory.go

+19-6
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,10 @@ func NewSingletonClients(info *types.ClusterInfo, logger logr.Logger) (*Singleto
166166
}
167167

168168
func (s *SingletonClients) clientCacheKeyForMapping(mapping *meta.RESTMapping) string {
169-
return fmt.Sprintf("%s+%s:%s",
170-
mapping.Resource.String(), mapping.GroupVersionKind.String(), mapping.Scope.Name())
169+
return fmt.Sprintf(
170+
"%s+%s:%s",
171+
mapping.Resource.String(), mapping.GroupVersionKind.String(), mapping.Scope.Name(),
172+
)
171173
}
172174

173175
func (s *SingletonClients) DynamicResourceInterface(obj *unstructured.Unstructured) (dynamic.ResourceInterface, error) {
@@ -191,7 +193,8 @@ func (s *SingletonClients) DynamicResourceInterface(obj *unstructured.Unstructur
191193
if namespace != "" {
192194
// TODO: Differentiate between server-fixable vs client-fixable errors?
193195
return nil, fmt.Errorf(
194-
"namespace %q was provided for cluster-scoped object %v", obj.GetNamespace(), gvk)
196+
"namespace %q was provided for cluster-scoped object %v", obj.GetNamespace(), gvk,
197+
)
195198
}
196199
dynamicResource = s.dynamicClient.Resource(mapping.Resource)
197200
default:
@@ -207,9 +210,19 @@ func (s *SingletonClients) ResourceInfo(obj *unstructured.Unstructured, retryOnN
207210
return nil, err
208211
}
209212
info := &resource.Info{}
210-
clnt, err := s.ClientForMapping(mapping)
211-
if err != nil {
212-
return nil, err
213+
214+
var clnt resource.RESTClient
215+
if s.Scheme().IsGroupRegistered(mapping.GroupVersionKind.Group) {
216+
clnt, err = s.ClientForMapping(mapping)
217+
if err != nil {
218+
return nil, err
219+
}
220+
} else {
221+
clnt, err = s.UnstructuredClientForMapping(mapping)
222+
if err != nil {
223+
return nil, err
224+
}
225+
obj.SetGroupVersionKind(mapping.GroupVersionKind)
213226
}
214227

215228
info.Client = clnt

pkg/client/util.go

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package client
22

33
import (
4-
"fmt"
5-
64
"k8s.io/apimachinery/pkg/api/meta"
75
"k8s.io/apimachinery/pkg/runtime"
86
)
@@ -20,8 +18,7 @@ func getResourceMapping(obj runtime.Object, mapper meta.RESTMapper, retryOnNoMat
2018
// return second call after reset
2119
mapping, err = mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
2220
if err != nil {
23-
return nil, fmt.Errorf("rest mapping for %s could not be resolved after reset: %w",
24-
obj.GetObjectKind().GroupVersionKind().String(), err)
21+
return nil, err
2522
}
2623
}
2724

pkg/declarative/v2/cleanup.go

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package v2
2+
3+
import (
4+
"context"
5+
"errors"
6+
7+
"github.com/kyma-project/module-manager/pkg/types"
8+
apierrors "k8s.io/apimachinery/pkg/api/errors"
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
"k8s.io/cli-runtime/pkg/resource"
11+
"sigs.k8s.io/controller-runtime/pkg/client"
12+
)
13+
14+
var ErrDeletionNotFinished = errors.New("deletion is not yet finished")
15+
16+
type Cleanup interface {
17+
Run(context.Context, []*resource.Info) error
18+
}
19+
20+
type ConcurrentCleanup struct {
21+
clnt client.Client
22+
policy client.PropagationPolicy
23+
}
24+
25+
func NewConcurrentCleanup(clnt client.Client) Cleanup {
26+
return &ConcurrentCleanup{clnt: clnt, policy: client.PropagationPolicy(metav1.DeletePropagationBackground)}
27+
}
28+
29+
func (c *ConcurrentCleanup) Run(ctx context.Context, infos []*resource.Info) error {
30+
// The Runtime Complexity of this Branch is N as only ServerSideApplier Patch is required
31+
results := make(chan error, len(infos))
32+
for i := range infos {
33+
i := i
34+
go c.cleanupResource(ctx, infos[i], results)
35+
}
36+
37+
var errs []error
38+
present := len(infos)
39+
for i := 0; i < len(infos); i++ {
40+
err := <-results
41+
if apierrors.IsNotFound(err) {
42+
present--
43+
continue
44+
}
45+
if err != nil {
46+
errs = append(errs, err)
47+
}
48+
}
49+
50+
if len(errs) > 0 {
51+
return types.NewMultiError(errs)
52+
}
53+
54+
if present > 0 {
55+
return ErrDeletionNotFinished
56+
}
57+
return nil
58+
}
59+
60+
func (c *ConcurrentCleanup) cleanupResource(ctx context.Context, info *resource.Info, results chan error) {
61+
results <- c.clnt.Delete(ctx, info.Object.(client.Object), c.policy)
62+
}

pkg/declarative/v2/client.go

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package v2
2+
3+
import (
4+
"helm.sh/helm/v3/pkg/action"
5+
"helm.sh/helm/v3/pkg/kube"
6+
"k8s.io/cli-runtime/pkg/resource"
7+
"sigs.k8s.io/controller-runtime/pkg/client"
8+
)
9+
10+
type Client interface {
11+
kube.Factory
12+
Install() *action.Install
13+
KubeClient() *kube.Client
14+
15+
resource.RESTClientGetter
16+
ResourceInfoConverter
17+
18+
client.Client
19+
}

0 commit comments

Comments
 (0)