Skip to content

Commit 4736b32

Browse files
CR-7598 (#180)
* initial commit * constants for reqs and improved validation logic * removed redundant * bump * error msg * group in store * resolved pr comments * bump * codegen * rename as kubeutil * using ... instead of loop
1 parent 6aad951 commit 4736b32

File tree

6 files changed

+230
-16
lines changed

6 files changed

+230
-16
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
VERSION=v0.0.167
1+
VERSION=v0.0.168
22

33
OUT_DIR=dist
44
YEAR?=$(shell date +"%Y")

cmd/commands/runtime.go

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import (
4747
argowf "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow"
4848

4949
"github.com/Masterminds/semver/v3"
50+
kubeutil "github.com/codefresh-io/cli-v2/pkg/util/kube"
5051
"github.com/ghodss/yaml"
5152
"github.com/go-git/go-billy/v5/memfs"
5253
billyUtils "github.com/go-git/go-billy/v5/util"
@@ -75,9 +76,8 @@ type (
7576
GitIntegrationOpts *apmodel.AddGitIntegrationArgs
7677
KubeFactory kube.Factory
7778
CommonConfig *runtime.CommonConfig
78-
79-
versionStr string
80-
kubeContext string
79+
versionStr string
80+
kubeContext string
8181
}
8282
RuntimeUninstallOptions struct {
8383
RuntimeName string
@@ -498,6 +498,11 @@ func preInstallationChecks(ctx context.Context, opts *RuntimeInstallOptions) err
498498
return fmt.Errorf("existing runtime check failed: %w", err)
499499
}
500500

501+
err := kubeutil.EnsureClusterRequirements(ctx, opts.KubeFactory, opts.RuntimeName)
502+
if err != nil {
503+
return fmt.Errorf(fmt.Sprintf("validation of minimum cluster requirements failed: %v ", err))
504+
}
505+
501506
return nil
502507
}
503508

@@ -555,7 +560,7 @@ func checkExistingRuntimes(ctx context.Context, runtime string) error {
555560
}
556561

557562
func intervalCheckIsRuntimePersisted(ctx context.Context, runtimeName string) error {
558-
maxRetries := 180 // up to 30 min
563+
maxRetries := 180 // up to 30 min
559564
waitMsg := "Waiting for the runtime installation to complete"
560565
stop := util.WithSpinner(ctx, waitMsg)
561566
ticker := time.NewTicker(time.Second * 10)
@@ -976,10 +981,10 @@ func createWorkflowsIngress(ctx context.Context, cloneOpts *git.CloneOptions, rt
976981
Name: rt.Name + store.Get().WorkflowsIngressName,
977982
Namespace: rt.Namespace,
978983
Annotations: map[string]string{
979-
"ingress.kubernetes.io/protocol": "https",
980-
"ingress.kubernetes.io/rewrite-target": "/$2",
984+
"ingress.kubernetes.io/protocol": "https",
985+
"ingress.kubernetes.io/rewrite-target": "/$2",
981986
"nginx.ingress.kubernetes.io/backend-protocol": "https",
982-
"nginx.ingress.kubernetes.io/rewrite-target": "/$2",
987+
"nginx.ingress.kubernetes.io/rewrite-target": "/$2",
983988
},
984989
Paths: []ingressutil.IngressPath{
985990
{

docs/releases/release_notes.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
### Installed Applications:
22

3-
- Argo CD [v2.1.3](https://github.com/codefresh-io/argo-cd/releases/tag/v2.1.3)
4-
- Argo CD ApplicationSet Controller [v0.2.0](https://github.com/argoproj-labs/applicationset/releases/tag/v0.2.0)
5-
- Argo Events [v1.4.0](https://github.com/argoproj/argo-events/releases/tag/v1.4.0)
6-
- Argo Rollouts [v1.1.0](https://github.com/argoproj/argo-rollouts/releases/tag/v1.1.0)
7-
- Argo Workflows [v3.1.8](https://github.com/argoproj/argo-workflows/releases/tag/v3.1.8)
3+
- Argo CD [v2.1.3](https://github.com/codefresh-io/argo-cd/releases/tag/v2.1.3)
4+
- Argo CD ApplicationSet Controller [v0.2.0](https://github.com/argoproj-labs/applicationset/releases/tag/v0.2.0)
5+
- Argo Events [v1.4.0](https://github.com/argoproj/argo-events/releases/tag/v1.4.0)
6+
- Argo Rollouts [v1.1.0](https://github.com/argoproj/argo-rollouts/releases/tag/v1.1.0)
7+
- Argo Workflows [v3.1.8](https://github.com/argoproj/argo-workflows/releases/tag/v3.1.8)
88

99
### Using brew:
1010

@@ -23,7 +23,7 @@ cf version
2323

2424
```bash
2525
# download and extract the binary
26-
curl -L --output - https://github.com/codefresh-io/cli-v2/releases/download/v0.0.167/cf-linux-amd64.tar.gz | tar zx
26+
curl -L --output - https://github.com/codefresh-io/cli-v2/releases/download/v0.0.168/cf-linux-amd64.tar.gz | tar zx
2727

2828
# move the binary to your $PATH
2929
mv ./cf-linux-amd64 /usr/local/bin/cf
@@ -36,7 +36,7 @@ cf version
3636

3737
```bash
3838
# download and extract the binary
39-
curl -L --output - https://github.com/codefresh-io/cli-v2/releases/download/v0.0.167/cf-darwin-amd64.tar.gz | tar zx
39+
curl -L --output - https://github.com/codefresh-io/cli-v2/releases/download/v0.0.168/cf-darwin-amd64.tar.gz | tar zx
4040

4141
# move the binary to your $PATH
4242
mv ./cf-darwin-amd64 /usr/local/bin/cf

manifests/runtime.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ metadata:
55
namespace: "{{ namespace }}"
66
spec:
77
defVersion: 1.0.0
8-
version: 0.0.167
8+
version: 0.0.168
99
bootstrapSpecifier: github.com/codefresh-io/cli-v2/manifests/argo-cd
1010
components:
1111
- name: events

pkg/store/store.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ type Store struct {
111111
GithubAccessTokenSecretKey string
112112
ArgoCD string
113113
Silent bool
114+
MinimumMemorySizeRequired string
115+
MinimumCpuRequired string
116+
MinimumLocalDiskSizeRequired string
114117
}
115118

116119
// Get returns the global store
@@ -183,6 +186,8 @@ func init() {
183186
s.GithubAccessTokenSecretObjectName = "autopilot-secret"
184187
s.GithubAccessTokenSecretKey = "git_token"
185188
s.ArgoCD = "argo-cd"
189+
s.MinimumMemorySizeRequired = "3000"
190+
s.MinimumCpuRequired = "2"
186191
initVersion()
187192
}
188193

pkg/util/kube/kube.go

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
// Copyright 2021 The Codefresh Authors.
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 kube
16+
17+
import (
18+
"context"
19+
"fmt"
20+
"strings"
21+
22+
"github.com/argoproj-labs/argocd-autopilot/pkg/kube"
23+
"github.com/codefresh-io/cli-v2/pkg/store"
24+
authv1 "k8s.io/api/authorization/v1"
25+
v1 "k8s.io/api/core/v1"
26+
"k8s.io/apimachinery/pkg/api/resource"
27+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28+
"k8s.io/client-go/kubernetes"
29+
)
30+
31+
type (
32+
rbacValidation struct {
33+
Namespace string
34+
Resource string
35+
Verbs []string
36+
Group string
37+
}
38+
39+
validationRequest struct {
40+
cpu string
41+
memorySize string
42+
rbac []rbacValidation
43+
}
44+
)
45+
46+
func EnsureClusterRequirements(ctx context.Context, kubeFactory kube.Factory, namespace string) error {
47+
requirementsValidationErrorMessage := "cluster does not meet minimum requirements"
48+
var specificErrorMessages []string
49+
50+
client, err := kubeFactory.KubernetesClientSet()
51+
if err != nil {
52+
return fmt.Errorf("cannot create kubernetes clientset: %v ", err)
53+
}
54+
55+
req := validationRequest{
56+
rbac: []rbacValidation{
57+
{
58+
Resource: "ServiceAccount",
59+
Verbs: []string{"create", "delete"},
60+
Namespace: namespace,
61+
},
62+
{
63+
Resource: "ConfigMap",
64+
Verbs: []string{"create", "update", "delete"},
65+
Namespace: namespace,
66+
},
67+
{
68+
Resource: "Service",
69+
Verbs: []string{"create", "update", "delete"},
70+
Namespace: namespace,
71+
},
72+
{
73+
Resource: "Role",
74+
Group: "rbac.authorization.k8s.io",
75+
Verbs: []string{"create", "update", "delete"},
76+
Namespace: namespace,
77+
},
78+
{
79+
Resource: "RoleBinding",
80+
Group: "rbac.authorization.k8s.io",
81+
Verbs: []string{"create", "update", "delete"},
82+
Namespace: namespace,
83+
},
84+
{
85+
Resource: "persistentvolumeclaims",
86+
Verbs: []string{"create", "update", "delete"},
87+
Namespace: namespace,
88+
},
89+
{
90+
Resource: "pods",
91+
Verbs: []string{"create", "update", "delete"},
92+
Namespace: namespace,
93+
},
94+
},
95+
memorySize: store.Get().MinimumMemorySizeRequired,
96+
cpu: store.Get().MinimumCpuRequired,
97+
}
98+
99+
specs := []*authv1.SelfSubjectAccessReview{}
100+
for _, rbac := range req.rbac {
101+
for _, verb := range rbac.Verbs {
102+
attr := &authv1.ResourceAttributes{
103+
Resource: rbac.Resource,
104+
Verb: verb,
105+
Group: rbac.Group,
106+
}
107+
if rbac.Namespace != "" {
108+
attr.Namespace = rbac.Namespace
109+
}
110+
specs = append(specs, &authv1.SelfSubjectAccessReview{
111+
Spec: authv1.SelfSubjectAccessReviewSpec{
112+
ResourceAttributes: attr,
113+
},
114+
})
115+
}
116+
}
117+
118+
rbacres := testRBAC(ctx, client, specs)
119+
if len(rbacres) > 0 {
120+
specificErrorMessages = append(specificErrorMessages, rbacres...)
121+
return fmt.Errorf("%s: failed testing rbac: %v", requirementsValidationErrorMessage, specificErrorMessages)
122+
}
123+
124+
nodes, err := client.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
125+
if err != nil {
126+
return fmt.Errorf("%s: failed getting nodes: %v", requirementsValidationErrorMessage, err)
127+
}
128+
129+
if len(nodes.Items) == 0 {
130+
return fmt.Errorf("%s: No nodes in cluster", requirementsValidationErrorMessage)
131+
}
132+
133+
atLeastOneMet := false
134+
for _, n := range nodes.Items {
135+
res := testNode(n, req)
136+
if len(res) > 0 {
137+
specificErrorMessages = append(specificErrorMessages, res...)
138+
} else {
139+
atLeastOneMet = true
140+
}
141+
}
142+
if !atLeastOneMet {
143+
return fmt.Errorf("%s: %v", requirementsValidationErrorMessage, specificErrorMessages)
144+
}
145+
146+
return nil
147+
}
148+
149+
func testRBAC(ctx context.Context, client kubernetes.Interface, specs []*authv1.SelfSubjectAccessReview) []string {
150+
res := []string{}
151+
for _, sar := range specs {
152+
resp, err := client.AuthorizationV1().SelfSubjectAccessReviews().Create(ctx, sar, metav1.CreateOptions{})
153+
if err != nil {
154+
res = append(res, err.Error())
155+
continue
156+
}
157+
if !resp.Status.Allowed {
158+
verb := sar.Spec.ResourceAttributes.Verb
159+
namespace := sar.Spec.ResourceAttributes.Namespace
160+
resource := sar.Spec.ResourceAttributes.Resource
161+
group := sar.Spec.ResourceAttributes.Group
162+
msg := strings.Builder{}
163+
msg.WriteString(fmt.Sprintf("Insufficient permission, %s %s/%s is not allowed", verb, group, resource))
164+
if namespace != "" {
165+
msg.WriteString(fmt.Sprintf(" on namespace %s", namespace))
166+
}
167+
res = append(res, msg.String())
168+
}
169+
}
170+
return res
171+
}
172+
173+
func testNode(n v1.Node, req validationRequest) []string {
174+
result := []string{}
175+
176+
if req.cpu != "" {
177+
requiredCPU, err := resource.ParseQuantity(req.cpu)
178+
if err != nil {
179+
result = append(result, err.Error())
180+
return result
181+
}
182+
cpu := n.Status.Capacity.Cpu()
183+
184+
if cpu != nil && cpu.Cmp(requiredCPU) == -1 {
185+
msg := fmt.Sprintf("Insufficiant CPU on node %s, current: %s - required: %s", n.GetObjectMeta().GetName(), cpu.String(), requiredCPU.String())
186+
result = append(result, msg)
187+
}
188+
}
189+
190+
if req.memorySize != "" {
191+
requiredMemory, err := resource.ParseQuantity(req.memorySize)
192+
if err != nil {
193+
result = append(result, err.Error())
194+
return result
195+
}
196+
memory := n.Status.Capacity.Memory()
197+
if memory != nil && memory.Cmp(requiredMemory) == -1 {
198+
msg := fmt.Sprintf("Insufficiant Memory on node %s, current: %s - required: %s", n.GetObjectMeta().GetName(), memory.String(), requiredMemory.String())
199+
result = append(result, msg)
200+
}
201+
}
202+
203+
return result
204+
}

0 commit comments

Comments
 (0)