Skip to content

Commit eb6ddb1

Browse files
Adds more robust testing for ebs csi drivers
1 parent 221067e commit eb6ddb1

File tree

8 files changed

+464
-196
lines changed

8 files changed

+464
-196
lines changed

go.mod

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,14 @@ require (
4646
github.com/Masterminds/sprig v2.15.0+incompatible // indirect
4747
github.com/alecthomas/chroma v0.10.0 // indirect
4848
github.com/aokoli/goutils v1.0.1 // indirect
49-
github.com/aws/aws-sdk-go-v2 v1.17.4 // indirect
49+
github.com/aws/aws-sdk-go-v2 v1.17.5 // indirect
5050
github.com/aws/aws-sdk-go-v2/credentials v1.13.13 // indirect
5151
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22 // indirect
52-
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28 // indirect
53-
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22 // indirect
52+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.29 // indirect
53+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.23 // indirect
5454
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29 // indirect
5555
github.com/aws/aws-sdk-go-v2/service/ec2 v1.86.0 // indirect
56+
github.com/aws/aws-sdk-go-v2/service/iam v1.19.3 // indirect
5657
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22 // indirect
5758
github.com/aws/aws-sdk-go-v2/service/sso v1.12.2 // indirect
5859
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.2 // indirect

go.sum

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5
3333
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
3434
github.com/aws/aws-sdk-go-v2 v1.17.4 h1:wyC6p9Yfq6V2y98wfDsj6OnNQa4w2BLGCLIxzNhwOGY=
3535
github.com/aws/aws-sdk-go-v2 v1.17.4/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
36+
github.com/aws/aws-sdk-go-v2 v1.17.5 h1:TzCUW1Nq4H8Xscph5M/skINUitxM5UBAyvm2s7XBzL4=
37+
github.com/aws/aws-sdk-go-v2 v1.17.5/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
3638
github.com/aws/aws-sdk-go-v2/config v1.18.13 h1:v0xlYqbO6/EVlM8tUn2QEOA7btQxcgidEq2JRDBPTho=
3739
github.com/aws/aws-sdk-go-v2/config v1.18.13/go.mod h1:r39wGSZB7wPDW1i54JyQXUpc5KsWjh5z/3S5D9eCqDg=
3840
github.com/aws/aws-sdk-go-v2/credentials v1.13.13 h1:zw1KAc1kl00NYd3ofVmFrb09qnYlSQMeh+fmlQRAihI=
@@ -41,14 +43,20 @@ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22 h1:3aMfcTmoXtTZnaT86QlVaY
4143
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22/go.mod h1:YGSIJyQ6D6FjKMQh16hVFSIUD54L4F7zTGePqYMYYJU=
4244
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28 h1:r+XwaCLpIvCKjBIYy/HVZujQS9tsz5ohHG3ZIe0wKoE=
4345
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28/go.mod h1:3lwChorpIM/BhImY/hy+Z6jekmN92cXGPI1QJasVPYY=
46+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.29 h1:9/aKwwus0TQxppPXFmf010DFrE+ssSbzroLVYINA+xE=
47+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.29/go.mod h1:Dip3sIGv485+xerzVv24emnjX5Sg88utCL8fwGmCeWg=
4448
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22 h1:7AwGYXDdqRQYsluvKFmWoqpcOQJ4bH634SkYf3FNj/A=
4549
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22/go.mod h1:EqK7gVrIGAHyZItrD1D8B0ilgwMD1GiWAmbU4u/JHNk=
50+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.23 h1:b/Vn141DBuLVgXbhRWIrl9g+ww7G+ScV5SzniWR13jQ=
51+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.23/go.mod h1:mr6c4cHC+S/MMkrjtSlG4QA36kOznDep+0fga5L/fGQ=
4652
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29 h1:J4xhFd6zHhdF9jPP0FQJ6WknzBboGMBNjKOv4iTuw4A=
4753
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29/go.mod h1:TwuqRBGzxjQJIwH16/fOZodwXt2Zxa9/cwJC5ke4j7s=
4854
github.com/aws/aws-sdk-go-v2/service/ec2 v1.86.0 h1:4dt0Mg5veHbMLcA2JAR9LDxvqXjtG0ZLQdxZG/2wHy4=
4955
github.com/aws/aws-sdk-go-v2/service/ec2 v1.86.0/go.mod h1:jK4MhMMe6HIe4qnjGaQqQQECcsxRZ0q86oCq06T8IEE=
5056
github.com/aws/aws-sdk-go-v2/service/eks v1.27.3 h1:jlh0AJVhauqSGaiwRJx4j0LNBGEAaGn46sA1XJyTj0E=
5157
github.com/aws/aws-sdk-go-v2/service/eks v1.27.3/go.mod h1:S30WtE6uWErcppqG9Rl03MATEFYsm0vnDyxPUKmmCqU=
58+
github.com/aws/aws-sdk-go-v2/service/iam v1.19.3 h1:vaDXu/p/5RNK++B2pqKS3hwsWGxsVjJJqPaAMg0OkiM=
59+
github.com/aws/aws-sdk-go-v2/service/iam v1.19.3/go.mod h1:F5Xt96+AfAiyMpRXHy9CKafE/KULVwj7MwgZ0a4row4=
5260
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22 h1:LjFQf8hFuMO22HkV5VWGLBvmCLBCLPivUAmpdpnp4Vs=
5361
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22/go.mod h1:xt0Au8yPIwYXf/GYPy/vl4K3CgwhfQMYbrH7DlUUIws=
5462
github.com/aws/aws-sdk-go-v2/service/sso v1.12.2 h1:EN102fWY7hI5u/2FPheTrwwMHkSXfl49RYkeEnJsrCU=

internal/validate/kube/eks.go

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
package kube
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"path/filepath"
7+
"reflect"
8+
"strings"
9+
10+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11+
"k8s.io/client-go/kubernetes"
12+
"k8s.io/client-go/tools/clientcmd"
13+
"k8s.io/client-go/util/homedir"
14+
15+
"github.com/aws/aws-sdk-go-v2/service/ec2"
16+
"github.com/aws/aws-sdk-go-v2/service/ec2/types"
17+
"github.com/aws/aws-sdk-go-v2/service/eks"
18+
"github.com/aws/aws-sdk-go-v2/service/iam"
19+
"github.com/sourcegraph/src-cli/internal/validate"
20+
21+
"github.com/sourcegraph/sourcegraph/lib/errors"
22+
)
23+
24+
type EbsTestObjects struct {
25+
addons []string
26+
serviceAccount string
27+
ebsRolePolicy RolePolicy
28+
}
29+
30+
// EksEbsCsiDrivers will validate that EKS cluster has ebs-cli drivers installed and configured
31+
func EksEbsCsiDrivers(ctx context.Context, config *Config) ([]validate.Result, error) {
32+
var results []validate.Result
33+
var ebsTestParams EbsTestObjects
34+
35+
addons, err := getAddons(ctx, config.eksClient)
36+
if err != nil {
37+
results = append(results, validate.Result{
38+
Status: validate.Failure,
39+
Message: "EKS: could not validate ebs in addons",
40+
})
41+
return results, err
42+
}
43+
44+
ebsSA, err := getEbsSA(ctx, config.clientSet)
45+
if err != nil {
46+
results = append(results, validate.Result{
47+
Status: validate.Failure,
48+
Message: "EKS: could not validate ebs service account",
49+
})
50+
return results, err
51+
}
52+
53+
EBSCSIRole, err := getEBSCSIRole(ctx, config.iamClient, ebsSA)
54+
if err != nil {
55+
results = append(results, validate.Result{
56+
Status: validate.Failure,
57+
Message: "EKS: could not validate ebs role policy",
58+
})
59+
}
60+
61+
ebsTestParams.addons = addons
62+
ebsTestParams.serviceAccount = ebsSA
63+
ebsTestParams.ebsRolePolicy = EBSCSIRole
64+
65+
r := validateEbsCsiDrivers(ebsTestParams)
66+
results = append(results, r...)
67+
68+
return results, nil
69+
}
70+
71+
func validateEbsCsiDrivers(testers EbsTestObjects) (result []validate.Result) {
72+
for _, addon := range testers.addons {
73+
if addon == "aws-ebs-csi-driver" {
74+
result = append(result, validate.Result{
75+
Status: validate.Success,
76+
Message: "EKS: ebs-csi driver validated",
77+
})
78+
} else {
79+
result = append(result, validate.Result{
80+
Status: validate.Failure,
81+
Message: "EKS: no 'aws-ebs-csi-driver' present in addons",
82+
})
83+
}
84+
}
85+
86+
if testers.serviceAccount != "" {
87+
result = append(result, validate.Result{
88+
Status: validate.Success,
89+
Message: "EKS: ebs service account validated",
90+
})
91+
} else {
92+
result = append(result, validate.Result{
93+
Status: validate.Failure,
94+
Message: "EKS: no 'ebs-csi-controller-sa' service account present in cluster",
95+
})
96+
}
97+
98+
if *testers.ebsRolePolicy.PolicyName == "AmazonEBSCSIDriverPolicy" {
99+
result = append(result, validate.Result{
100+
Status: validate.Success,
101+
Message: "EKS: service account ebs role policy validated",
102+
})
103+
} else {
104+
result = append(result, validate.Result{
105+
Status: validate.Failure,
106+
Message: "EKS: no 'AmazonEBSCSIDriverPolicy' attached to role",
107+
})
108+
}
109+
110+
return result
111+
}
112+
113+
// EksVpc checks if a valid vpc available
114+
func EksVpc(ctx context.Context, config *Config) ([]validate.Result, error) {
115+
var results []validate.Result
116+
if config.ec2Client == nil {
117+
results = append(results, validate.Result{
118+
Status: validate.Failure,
119+
Message: "EKS: validate VPC failed",
120+
})
121+
}
122+
123+
inputs := &ec2.DescribeVpcsInput{}
124+
outputs, err := config.ec2Client.DescribeVpcs(ctx, inputs)
125+
126+
if err != nil {
127+
results = append(results, validate.Result{
128+
Status: validate.Failure,
129+
Message: "EKS: Validate VPC failed",
130+
})
131+
return results, nil
132+
}
133+
134+
if len(outputs.Vpcs) == 0 {
135+
results = append(results, validate.Result{
136+
Status: validate.Failure,
137+
Message: "EKS: Validate VPC failed: No VPC configured",
138+
})
139+
return results, nil
140+
}
141+
142+
for _, vpc := range outputs.Vpcs {
143+
r := validateVpc(&vpc)
144+
results = append(results, r...)
145+
}
146+
147+
return results, nil
148+
}
149+
150+
func validateVpc(vpc *types.Vpc) (result []validate.Result) {
151+
state := vpc.State
152+
153+
if state == "available" {
154+
result = append(result, validate.Result{
155+
Status: validate.Success,
156+
Message: "VPC is validated",
157+
})
158+
return result
159+
}
160+
161+
result = append(result, validate.Result{
162+
Status: validate.Failure,
163+
Message: "vpc.State stuck in pending state",
164+
})
165+
166+
return result
167+
}
168+
169+
// checks if current context is set to EKS cluster
170+
func CurrentContextSetToEKSCluster() error {
171+
home := homedir.HomeDir()
172+
pathToKubeConfig := filepath.Join(home, ".kube", "config")
173+
174+
config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
175+
&clientcmd.ClientConfigLoadingRules{ExplicitPath: pathToKubeConfig},
176+
&clientcmd.ConfigOverrides{
177+
CurrentContext: "",
178+
}).RawConfig()
179+
180+
if err != nil {
181+
return err
182+
}
183+
184+
got := strings.Split(config.CurrentContext, ":")
185+
want := []string{"arn", "aws", "eks"}
186+
187+
if len(got) >= 3 {
188+
got = got[:3]
189+
if reflect.DeepEqual(got, want) {
190+
return nil
191+
}
192+
}
193+
194+
return errors.Newf("%s no eks cluster configured", validate.FailureEmoji)
195+
}
196+
197+
func getAddons(ctx context.Context, client *eks.Client) ([]string, error) {
198+
clusterName := getClusterName(ctx, client)
199+
inputs := &eks.ListAddonsInput{ClusterName: clusterName}
200+
outputs, err := client.ListAddons(ctx, inputs)
201+
202+
if err != nil {
203+
return nil, err
204+
}
205+
206+
return outputs.Addons, nil
207+
}
208+
209+
func getEbsSA(ctx context.Context, client *kubernetes.Clientset) (string, error) {
210+
serviceAccounts := client.CoreV1().ServiceAccounts("kube-system")
211+
ebsSA, err := serviceAccounts.Get(
212+
ctx,
213+
"ebs-csi-controller-sa",
214+
metav1.GetOptions{},
215+
)
216+
217+
if err != nil {
218+
return "", err
219+
}
220+
221+
annotations := ebsSA.GetAnnotations()
222+
roleArn := strings.Split(annotations["eks.amazonaws.com/role-arn"], "/")
223+
ebsControllerSA := roleArn[len(roleArn)-1]
224+
225+
return ebsControllerSA, nil
226+
}
227+
228+
type RolePolicy struct {
229+
PolicyName *string
230+
PolicyArn *string
231+
}
232+
233+
func getEBSCSIRole(ctx context.Context, client *iam.Client, SAName string) (RolePolicy, error) {
234+
inputs := iam.ListAttachedRolePoliciesInput{RoleName: &SAName}
235+
outputs, err := client.ListAttachedRolePolicies(ctx, &inputs)
236+
237+
if err != nil {
238+
return RolePolicy{}, err
239+
}
240+
241+
var policyName *string
242+
for _, policy := range outputs.AttachedPolicies {
243+
policyName = policy.PolicyName
244+
if *policyName == "AmazonEBSCSIDriverPolicy" {
245+
return RolePolicy{
246+
PolicyName: policy.PolicyName,
247+
PolicyArn: policy.PolicyArn,
248+
}, nil
249+
}
250+
}
251+
252+
return RolePolicy{}, nil
253+
}
254+
255+
func getClusterName(ctx context.Context, client *eks.Client) *string {
256+
home := homedir.HomeDir()
257+
pathToKubeConfig := filepath.Join(home, ".kube", "config")
258+
259+
config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
260+
&clientcmd.ClientConfigLoadingRules{ExplicitPath: pathToKubeConfig},
261+
&clientcmd.ConfigOverrides{
262+
CurrentContext: "",
263+
}).RawConfig()
264+
265+
if err != nil {
266+
fmt.Printf("error while checking current context: %s\n", err)
267+
return nil
268+
}
269+
270+
currentContext := strings.Split(config.CurrentContext, "/")
271+
clusterName := currentContext[len(currentContext)-1]
272+
return &clusterName
273+
}

0 commit comments

Comments
 (0)