Skip to content

Commit 76f444b

Browse files
authored
[Feature] Add CRD installer (#922)
1 parent c372b99 commit 76f444b

File tree

12 files changed

+516
-0
lines changed

12 files changed

+516
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## [master](https://github.com/arangodb/kube-arangodb/tree/master) (N/A)
44
- (Feature) Improve Kubernetes clientsets management
55
- Migrate storage-operator CustomResourceDefinition apiVersion to apiextensions.k8s.io/v1
6+
- (Feature) Add CRD Installer
67

78
## [1.2.8](https://github.com/arangodb/kube-arangodb/tree/1.2.8) (2022-02-24)
89
- Do not check License V2 on Community images
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{{ if .Values.rbac.enabled -}}
2+
{{ if not (eq .Values.operator.scope "namespaced") -}}
3+
{{ if .Values.operator.enableCRDManagement -}}
4+
5+
apiVersion: rbac.authorization.k8s.io/v1
6+
kind: ClusterRoleBinding
7+
metadata:
8+
name: {{ template "kube-arangodb.rbac-cluster" . }}-crd
9+
labels:
10+
app.kubernetes.io/name: {{ template "kube-arangodb.name" . }}
11+
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
12+
app.kubernetes.io/managed-by: {{ .Release.Service }}
13+
app.kubernetes.io/instance: {{ .Release.Name }}
14+
release: {{ .Release.Name }}
15+
roleRef:
16+
apiGroup: rbac.authorization.k8s.io
17+
kind: ClusterRole
18+
name: {{ template "kube-arangodb.rbac-cluster" . }}-crd
19+
subjects:
20+
- kind: ServiceAccount
21+
name: {{ template "kube-arangodb.operatorName" . }}
22+
namespace: {{ .Release.Namespace }}
23+
24+
{{- end }}
25+
{{- end }}
26+
{{- end }}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{{ if .Values.rbac.enabled -}}
2+
{{ if not (eq .Values.operator.scope "namespaced") -}}
3+
{{ if .Values.operator.enableCRDManagement -}}
4+
5+
apiVersion: rbac.authorization.k8s.io/v1
6+
kind: ClusterRole
7+
metadata:
8+
name: {{ template "kube-arangodb.rbac-cluster" . }}-crd
9+
labels:
10+
app.kubernetes.io/name: {{ template "kube-arangodb.name" . }}
11+
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
12+
app.kubernetes.io/managed-by: {{ .Release.Service }}
13+
app.kubernetes.io/instance: {{ .Release.Name }}
14+
release: {{ .Release.Name }}
15+
rules:
16+
- apiGroups: ["apiextensions.k8s.io"]
17+
resources: ["customresourcedefinitions"]
18+
verbs: ["get", "list", "watch", "update", "delete"]
19+
resourceNames:
20+
- "arangoclustersynchronizations.database.arangodb.com"
21+
22+
{{- end }}
23+
{{- end }}
24+
{{- end }}

chart/kube-arangodb/values.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ operator:
3535
allowChaos: false
3636

3737
nodeSelector: {}
38+
39+
enableCRDManagement: true
3840

3941
features:
4042
deployment: true

cmd/main.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ import (
6161
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
6262
"k8s.io/client-go/tools/record"
6363

64+
"github.com/arangodb/kube-arangodb/pkg/crd"
6465
"github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/scheme"
6566
"github.com/arangodb/kube-arangodb/pkg/logging"
6667
"github.com/arangodb/kube-arangodb/pkg/operator"
@@ -120,6 +121,9 @@ var (
120121
singleMode bool
121122
scope string
122123
}
124+
crdOptions struct {
125+
install bool
126+
}
123127
operatorKubernetesOptions struct {
124128
maxBatchSize int64
125129

@@ -178,6 +182,7 @@ func init() {
178182
f.Int64Var(&operatorKubernetesOptions.maxBatchSize, "kubernetes.max-batch-size", globals.DefaultKubernetesRequestBatchSize, "Size of batch during objects read")
179183
f.Float32Var(&operatorKubernetesOptions.qps, "kubernetes.qps", kclient.DefaultQPS, "Number of queries per second for k8s API")
180184
f.IntVar(&operatorKubernetesOptions.burst, "kubernetes.burst", kclient.DefaultBurst, "Burst for the k8s API")
185+
f.BoolVar(&crdOptions.install, "crd.install", true, "Install missing CRD if access is possible")
181186
f.IntVar(&operatorBackup.concurrentUploads, "backup-concurrent-uploads", globals.DefaultBackupConcurrentUploads, "Number of concurrent uploads per deployment")
182187
features.Init(&cmdMain)
183188
}
@@ -275,6 +280,13 @@ func executeMain(cmd *cobra.Command, args []string) {
275280
cliLog.Fatal().Msg("Failed to get client")
276281
}
277282

283+
if crdOptions.install {
284+
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
285+
defer cancel()
286+
287+
crd.EnsureCRD(ctx, logService.MustGetLogger("crd"), client)
288+
}
289+
278290
secrets := client.Kubernetes().CoreV1().Secrets(namespace)
279291

280292
// Create operator

pkg/crd/apply.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
21+
package crd
22+
23+
import (
24+
"context"
25+
"fmt"
26+
27+
"github.com/arangodb/go-driver"
28+
"github.com/arangodb/kube-arangodb/pkg/util/kclient"
29+
"github.com/rs/zerolog"
30+
authorization "k8s.io/api/authorization/v1"
31+
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
32+
"k8s.io/apimachinery/pkg/api/errors"
33+
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
34+
)
35+
36+
func EnsureCRD(ctx context.Context, log zerolog.Logger, client kclient.Client) {
37+
crdsLock.Lock()
38+
defer crdsLock.Unlock()
39+
40+
for crd, spec := range crds {
41+
getAccess := verifyCRDAccess(ctx, client, crd, "get")
42+
43+
if !getAccess.Allowed {
44+
log.Info().Str("crd", crd).Msgf("Get Operations is not allowed. Continue")
45+
continue
46+
}
47+
48+
c, err := client.KubernetesExtensions().ApiextensionsV1().CustomResourceDefinitions().Get(ctx, crd, meta.GetOptions{})
49+
if err != nil {
50+
if !errors.IsNotFound(err) {
51+
log.Warn().Err(err).Str("crd", crd).Msgf("Get Operations is not allowed due to error. Continue")
52+
continue
53+
}
54+
55+
createAccess := verifyCRDAccess(ctx, client, crd, "create")
56+
57+
if !createAccess.Allowed {
58+
log.Info().Str("crd", crd).Msgf("Create Operations is not allowed but CRD is missing. Continue")
59+
continue
60+
}
61+
62+
c = &apiextensions.CustomResourceDefinition{
63+
ObjectMeta: meta.ObjectMeta{
64+
Name: crd,
65+
Labels: map[string]string{
66+
Version: string(spec.version),
67+
},
68+
},
69+
Spec: spec.spec,
70+
}
71+
72+
if _, err := client.KubernetesExtensions().ApiextensionsV1().CustomResourceDefinitions().Create(ctx, c, meta.CreateOptions{}); err != nil {
73+
log.Warn().Err(err).Str("crd", crd).Msgf("Create Operations is not allowed due to error. Continue")
74+
continue
75+
}
76+
77+
log.Info().Str("crd", crd).Msgf("CRD Created")
78+
continue
79+
}
80+
81+
updateAccess := verifyCRDAccess(ctx, client, crd, "update")
82+
83+
if !updateAccess.Allowed {
84+
log.Info().Str("crd", crd).Msgf("Update Operations is not allowed. Continue")
85+
continue
86+
}
87+
88+
if c.ObjectMeta.Labels == nil {
89+
c.ObjectMeta.Labels = map[string]string{}
90+
}
91+
92+
if v, ok := c.ObjectMeta.Labels[Version]; ok {
93+
if v != "" {
94+
if !isUpdateRequired(spec.version, driver.Version(v)) {
95+
log.Info().Str("crd", crd).Msgf("CRD Update not required")
96+
continue
97+
}
98+
}
99+
}
100+
101+
c.ObjectMeta.Labels[Version] = string(spec.version)
102+
103+
c.Spec = spec.spec
104+
105+
if _, err := client.KubernetesExtensions().ApiextensionsV1().CustomResourceDefinitions().Update(ctx, c, meta.UpdateOptions{}); err != nil {
106+
log.Warn().Err(err).Str("crd", crd).Msgf("Create Operations is not allowed due to error. Continue")
107+
continue
108+
}
109+
log.Info().Str("crd", crd).Msgf("CRD Updated")
110+
}
111+
}
112+
113+
func verifyCRDAccess(ctx context.Context, client kclient.Client, crd string, verb string) authorization.SubjectAccessReviewStatus {
114+
r, err := verifyCRDAccessRequest(ctx, client, crd, verb)
115+
if err != nil {
116+
return authorization.SubjectAccessReviewStatus{
117+
Allowed: false,
118+
Reason: fmt.Sprintf("Unable to check access: %s", err.Error()),
119+
}
120+
}
121+
122+
return r.Status
123+
}
124+
125+
func verifyCRDAccessRequest(ctx context.Context, client kclient.Client, crd string, verb string) (*authorization.SelfSubjectAccessReview, error) {
126+
review := authorization.SelfSubjectAccessReview{
127+
Spec: authorization.SelfSubjectAccessReviewSpec{
128+
ResourceAttributes: &authorization.ResourceAttributes{
129+
Verb: verb,
130+
Group: "apiextensions.k8s.io",
131+
Version: "v1",
132+
Resource: "customresourcedefinitions",
133+
Name: crd,
134+
},
135+
},
136+
}
137+
138+
return client.Kubernetes().AuthorizationV1().SelfSubjectAccessReviews().Create(ctx, &review, meta.CreateOptions{})
139+
}

pkg/crd/apply_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
21+
package crd
22+
23+
import (
24+
"context"
25+
"testing"
26+
27+
"github.com/arangodb/kube-arangodb/pkg/util/kclient"
28+
"github.com/rs/zerolog/log"
29+
"github.com/stretchr/testify/require"
30+
)
31+
32+
func Test_Apply(t *testing.T) {
33+
t.Run("Ensure CRD exists", func(t *testing.T) {
34+
c, ok := kclient.GetDefaultFactory().Client()
35+
require.True(t, ok)
36+
37+
EnsureCRD(context.Background(), log.Logger, c)
38+
})
39+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
21+
package crd
22+
23+
import (
24+
"github.com/arangodb/kube-arangodb/pkg/util"
25+
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
26+
)
27+
28+
func init() {
29+
registerCRDWithPanic("arangoclustersynchronizations.database.arangodb.com", crd{
30+
version: "1.0.1",
31+
spec: apiextensions.CustomResourceDefinitionSpec{
32+
Group: "database.arangodb.com",
33+
Names: apiextensions.CustomResourceDefinitionNames{
34+
Plural: "arangoclustersynchronizations",
35+
Singular: "arangoclustersynchronization",
36+
ShortNames: []string{
37+
"arangoclustersync",
38+
},
39+
Kind: "ArangoClusterSynchronization",
40+
ListKind: "ArangoClusterSynchronizationList",
41+
},
42+
Scope: apiextensions.NamespaceScoped,
43+
Versions: []apiextensions.CustomResourceDefinitionVersion{
44+
{
45+
Name: "v1",
46+
Schema: &apiextensions.CustomResourceValidation{
47+
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
48+
Type: "object",
49+
XPreserveUnknownFields: util.NewBool(true),
50+
},
51+
},
52+
Served: true,
53+
Storage: true,
54+
Subresources: &apiextensions.CustomResourceSubresources{
55+
Status: &apiextensions.CustomResourceSubresourceStatus{},
56+
},
57+
},
58+
{
59+
Name: "v2alpha1",
60+
Schema: &apiextensions.CustomResourceValidation{
61+
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
62+
Type: "object",
63+
XPreserveUnknownFields: util.NewBool(true),
64+
},
65+
},
66+
Served: true,
67+
Storage: false,
68+
Subresources: &apiextensions.CustomResourceSubresources{
69+
Status: &apiextensions.CustomResourceSubresourceStatus{},
70+
},
71+
},
72+
},
73+
},
74+
})
75+
}

0 commit comments

Comments
 (0)