From 9045dda337d6cea44df03d1190c55b024a760660 Mon Sep 17 00:00:00 2001 From: lreciomelero <120394823+lreciomelero@users.noreply.github.com> Date: Thu, 5 Oct 2023 11:49:49 +0200 Subject: [PATCH 1/6] Feature/add infra validations (#335) * version v0.18.0-alpha * update docs for v0.17.0 * fix kind version in readme * comments-update-buildcontext * Added validations for azs, k8sVersion, vpcs and subnets in each provider * fixing dependency bugs * Merge with master * fixed gcp * Update pkg/cluster/internal/validate/gcp.go Co-authored-by: esierra-stratio <102975650+esierra-stratio@users.noreply.github.com> * fixed build * Update CHANGELOG.md --------- Co-authored-by: Benjamin Elder Co-authored-by: Daman Co-authored-by: Kubernetes Prow Robot Co-authored-by: esierra-stratio <102975650+esierra-stratio@users.noreply.github.com> --- CHANGELOG.md | 2 +- go.mod | 21 ++- go.sum | 24 ++- .../create/actions/createworker/azure.go | 2 +- pkg/cluster/internal/validate/aws.go | 79 +++++++++ pkg/cluster/internal/validate/azure.go | 117 ++++++++++++- pkg/cluster/internal/validate/common.go | 14 ++ pkg/cluster/internal/validate/gcp.go | 155 +++++++++++++++++- pkg/cluster/internal/validate/validate.go | 4 +- pkg/cmd/kind/version/version.go | 1 + 10 files changed, 392 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df70a558c5..07e666c26f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## 0.17.0-0.4.0 (upcoming) -* Upcoming changelog +* Added infrastructure validations for azs, vpcs, subnets and k8s versions ## 0.17.0-0.3.0 (2023-09-14) diff --git a/go.mod b/go.mod index 287007696c..5c0e0440e0 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module sigs.k8s.io/kind go 1.19 require ( - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.5.0 - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.2 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.1 github.com/BurntSushi/toml v1.0.0 github.com/alessio/shellescape v1.4.1 github.com/apparentlymart/go-cidr v1.1.0 @@ -22,8 +22,8 @@ require ( github.com/sosedoff/ansible-vault-go v0.1.1 github.com/spf13/cobra v1.4.0 github.com/spf13/pflag v1.0.5 - golang.org/x/crypto v0.6.0 // indirect - golang.org/x/term v0.6.0 + golang.org/x/crypto v0.12.0 // indirect + golang.org/x/term v0.11.0 google.golang.org/api v0.114.0 gopkg.in/yaml.v3 v3.0.1 sigs.k8s.io/yaml v1.3.0 @@ -31,18 +31,21 @@ require ( require ( github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v3 v3.0.0 - github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.2.1 github.com/aws/aws-sdk-go-v2 v1.19.0 github.com/aws/aws-sdk-go-v2/service/ec2 v1.105.1 golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 golang.org/x/oauth2 v0.6.0 ) +require github.com/golang-jwt/jwt/v5 v5.0.0 // indirect + require ( cloud.google.com/go/compute v1.18.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v0.9.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4 v4.2.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.2.0 + github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.0 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.35 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.29 // indirect @@ -66,9 +69,9 @@ require ( github.com/leodido/go-urn v1.2.1 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect go.opencensus.io v0.24.0 // indirect - golang.org/x/net v0.8.0 // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/text v0.8.0 // indirect + golang.org/x/net v0.14.0 // indirect + golang.org/x/sys v0.11.0 // indirect + golang.org/x/text v0.12.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect google.golang.org/grpc v1.53.0 // indirect diff --git a/go.sum b/go.sum index 31519e9964..966adf6ae0 100644 --- a/go.sum +++ b/go.sum @@ -7,18 +7,26 @@ cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2Aawl cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.5.0 h1:xGLAFFd9D3iLGxYiUGPdITSzsFmU1K8VtfuUHWAoN7M= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.5.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1 h1:/iHxaJhsFr0+xVFfbMr5vxz848jyiWuIEDhYq3y5odY= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.2 h1:uqM+VoHjVH6zdlkLF2b6O0ZANcHoj3rO0PoQ3jglUJA= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.2/go.mod h1:twTKAa1E6hLmSDjLhaCkbTMQKc7p/rNLU40rLxGEOCI= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.1 h1:LNHhpdK7hzUcx/k1LIcuh5k7k1LGIWLQfCjaneSj7Fc= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.1/go.mod h1:uE9zaUfEQT/nbQjVi2IblCG9iaLtZsuYZ8ne+PuQ02M= github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v3 v3.0.0 h1:n52GQBJBSxDM2ev9etx1jDpib1cj6mojOLfBCBajwCI= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v3 v3.0.0/go.mod h1:JZHrk5tfE4/xpxweWhcG3PafI/PV9ULUSltVBBFG9N4= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2 h1:mLY+pNLjCUeKhgnAJWAKhEUQM+RJQo2H1fuGSw1Ky1E= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.2.1 h1:bWh0Z2rOEDfB/ywv/l0iHN1JgyazE6kW/aIA89+CEK0= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.2.1/go.mod h1:Bzf34hhAE9NSxailk8xVeLEZbUjOXcC+GnU1mMKdhLw= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4 v4.2.0 h1:iGj7n4SmssnseLryJRs/0lb4Db129ioYOCPSPC+vEsw= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4 v4.2.0/go.mod h1:qeBrdANBgW4QsU1bF5/9qjrPRwFIt+AnOMxyH5Bwkhk= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 h1:ECsQtyERDVz3NP3kvDOTLvbQhqWp/x9EsGKtb4ogUr8= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.2.0 h1:Pmy0+3ox1IC3sp6musv87BFPIdQbqyPFjn7I8I0o2Js= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.2.0/go.mod h1:ThfyMjs6auYrWPnYJjI3H4H++oVPrz01pizpu8lfl3A= github.com/AzureAD/microsoft-authentication-library-for-go v0.9.0 h1:UE9n9rkJF62ArLb1F3DEjRt8O3jLwMWdSoypKV4f3MU= github.com/AzureAD/microsoft-authentication-library-for-go v0.9.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= +github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 h1:WpB/QDNLpMw72xHJc34BNNykqSOeEJDAWkhf0u12/Jk= +github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU= github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= @@ -85,6 +93,8 @@ github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJ github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= +github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -182,6 +192,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o= golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= @@ -198,6 +210,8 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw= golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= @@ -215,9 +229,13 @@ golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -225,6 +243,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= diff --git a/pkg/cluster/internal/create/actions/createworker/azure.go b/pkg/cluster/internal/create/actions/createworker/azure.go index 113c6d8eac..7749067e64 100644 --- a/pkg/cluster/internal/create/actions/createworker/azure.go +++ b/pkg/cluster/internal/create/actions/createworker/azure.go @@ -27,7 +27,7 @@ import ( "strings" "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" - "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4" "gopkg.in/yaml.v3" "sigs.k8s.io/kind/pkg/cluster/nodes" "sigs.k8s.io/kind/pkg/commons" diff --git a/pkg/cluster/internal/validate/aws.go b/pkg/cluster/internal/validate/aws.go index 383f00da10..dace24d601 100644 --- a/pkg/cluster/internal/validate/aws.go +++ b/pkg/cluster/internal/validate/aws.go @@ -28,6 +28,7 @@ import ( "github.com/apparentlymart/go-cidr/cidr" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2/types" "golang.org/x/exp/slices" "sigs.k8s.io/kind/pkg/commons" "sigs.k8s.io/kind/pkg/errors" @@ -51,6 +52,11 @@ func validateAWS(spec commons.Spec, providerSecrets map[string]string) error { return err } + azs, err := getAwsAzs(ctx, cfg, spec.Region) + if err != nil { + return err + } + if (spec.StorageClass != commons.StorageClass{}) { if err = validateAWSStorageClass(spec.StorageClass, spec.WorkerNodes); err != nil { return errors.Wrap(err, "spec.storageclass: Invalid value") @@ -102,6 +108,13 @@ func validateAWS(spec commons.Spec, providerSecrets map[string]string) error { return errors.New("spec.worker_nodes." + wn.Name + ": \"node_image\": must have the format " + AWSNodeImageFormat) } } + if wn.AZ != "" { + if len(azs) > 0 { + if !commons.Contains(azs, wn.AZ) { + return errors.New(wn.AZ + " does not exist in this region, azs: " + fmt.Sprint(azs)) + } + } + } if err := validateVolumeType(wn.RootVolume.Type, AWSVolumes); err != nil { return errors.Wrap(err, "spec.worker_nodes."+wn.Name+".root_volume: Invalid value: \"type\"") } @@ -139,8 +152,21 @@ func validateAWSNetwork(ctx context.Context, cfg aws.Config, spec commons.Spec) } } if spec.Networks.VPCID != "" { + vpcs, _ := getAwsVPCs(cfg) + if len(vpcs) > 0 && !commons.Contains(vpcs, spec.Networks.VPCID) { + return errors.New("\"vpc_id\": " + spec.Networks.VPCID + " does not exist") + } if len(spec.Networks.Subnets) == 0 { return errors.New("\"subnets\": are required when \"vpc_id\" is set") + } else { + subnets, _ := getAwsSubnets(spec.Networks.VPCID, cfg) + if len(subnets) > 0 { + for _, subnet := range spec.Networks.Subnets { + if !commons.Contains(subnets, subnet.SubnetId) { + return errors.New("\"subnets\": " + subnet.SubnetId + " does not belong to vpc with id: " + spec.Networks.VPCID) + } + } + } } } else { if len(spec.Networks.Subnets) > 0 { @@ -192,6 +218,44 @@ func validateAWSPodsNetwork(podsNetwork string) error { return nil } +func getAwsVPCs(config aws.Config) ([]string, error) { + vpcs := []string{} + + client := ec2.NewFromConfig(config) + DescribeVpcOpts := &ec2.DescribeVpcsInput{} + output, err := client.DescribeVpcs(context.Background(), DescribeVpcOpts) + if err != nil { + return []string{}, err + } + for _, vpc := range output.Vpcs { + vpcs = append(vpcs, *vpc.VpcId) + } + return vpcs, nil +} + +func getAwsSubnets(vpcId string, config aws.Config) ([]string, error) { + subnets := []string{} + + client := ec2.NewFromConfig(config) + vpc_id_filterName := "vpc-id" + DescribeSubnetOpts := &ec2.DescribeSubnetsInput{ + Filters: []types.Filter{ + { + Name: &vpc_id_filterName, + Values: []string{vpcId}, + }, + }, + } + output, err := client.DescribeSubnets(context.Background(), DescribeSubnetOpts) + if err != nil { + return []string{}, err + } + for _, subnet := range output.Subnets { + subnets = append(subnets, *subnet.SubnetId) + } + return subnets, nil +} + func validateAWSStorageClass(sc commons.StorageClass, wn commons.WorkerNodes) error { var err error var isKeyValid = regexp.MustCompile(`^arn:aws:kms:[a-zA-Z0-9-]+:\d{12}:key/[\w-]+$`).MatchString @@ -309,3 +373,18 @@ func validateAWSAZs(ctx context.Context, cfg aws.Config, spec commons.Spec) erro return nil } + +func getAwsAzs(ctx context.Context, cfg aws.Config, region string) ([]string, error) { + var azs []string + svc := ec2.NewFromConfig(cfg) + result, err := svc.DescribeAvailabilityZones(ctx, &ec2.DescribeAvailabilityZonesInput{}) + if err != nil { + return nil, err + } + for _, az := range result.AvailabilityZones { + if *az.RegionName == region { + azs = append(azs, *az.ZoneName) + } + } + return azs, nil +} diff --git a/pkg/cluster/internal/validate/azure.go b/pkg/cluster/internal/validate/azure.go index 7128d66909..8c1a240f2c 100644 --- a/pkg/cluster/internal/validate/azure.go +++ b/pkg/cluster/internal/validate/azure.go @@ -27,6 +27,8 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v3" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions" "golang.org/x/exp/slices" "sigs.k8s.io/kind/pkg/commons" "sigs.k8s.io/kind/pkg/errors" @@ -44,13 +46,17 @@ var isAzureIdentity = regexp.MustCompile(`(?i)^\/subscriptions\/[\w-]+\/resource var AzureIdentityFormat = "/subscriptions/[SUBSCRIPTION_ID]/resourceGroups/[RESOURCE_GROUP]/providers/Microsoft.ManagedIdentity/userAssignedIdentities/[IDENTITY_NAME]" var isPremium = regexp.MustCompile(`^(Premium|Ultra).*$`).MatchString -func validateAzure(spec commons.Spec, providerSecrets map[string]string) error { +func validateAzure(spec commons.Spec, providerSecrets map[string]string, clusterName string) error { var err error creds, err := validateAzureCredentials(providerSecrets) if err != nil { return err } + azs, err := getAzureAzs(creds, providerSecrets["SubscriptionID"], spec.Region) + if err != nil { + return err + } if (spec.StorageClass != commons.StorageClass{}) { if err = validateAzureStorageClass(spec.StorageClass, spec.WorkerNodes); err != nil { @@ -58,7 +64,7 @@ func validateAzure(spec commons.Spec, providerSecrets map[string]string) error { } } if !reflect.ValueOf(spec.Networks).IsZero() { - if err = validateAzureNetwork(spec.Networks, spec.ControlPlane.Managed); err != nil { + if err = validateAzureNetwork(spec.Networks, spec, creds, providerSecrets["SubscriptionID"], clusterName); err != nil { return errors.Wrap(err, "spec.networks: Invalid value") } } @@ -120,6 +126,13 @@ func validateAzure(spec commons.Spec, providerSecrets map[string]string) error { if err := validateVolumeType(wn.RootVolume.Type, AzureVolumes); err != nil { return errors.Wrap(err, "spec.worker_nodes."+wn.Name+".root_volume: Invalid value: \"type\"") } + if wn.AZ != "" { + if len(azs) > 0 { + if !commons.Contains(azs, wn.AZ) { + return errors.New(wn.AZ + " does not exist in this region, azs: " + fmt.Sprint(azs)) + } + } + } premiumStorage := hasAzurePremiumStorage(wn.Size) if isPremium(wn.RootVolume.Type) && !premiumStorage { return errors.New("spec.worker_nodes." + wn.Name + ".root_volume: Invalid value: \"type\": size doesn't support premium storage") @@ -221,12 +234,24 @@ func validateAzureStorageClass(sc commons.StorageClass, wn commons.WorkerNodes) return nil } -func validateAzureNetwork(network commons.Networks, managed bool) error { +func validateAzureNetwork(network commons.Networks, spec commons.Spec, creds *azidentity.ClientSecretCredential, subscription string, clusterName string) error { + rg := clusterName if network.VPCID != "" { + + if spec.Networks.ResourceGroup != "" { + rg = spec.Networks.ResourceGroup + } + vpcs, err := getAzureVpcs(creds, subscription, spec.Region, rg) + if err != nil { + return err + } + if len(vpcs) > 0 && !commons.Contains(vpcs, network.VPCID) { + return errors.New("\"vpc_id\": " + network.VPCID + " does not exist in this resourceGroup") + } if len(network.Subnets) == 0 { return errors.New("\"subnets\": are required when \"vpc_id\" is set") } - if managed && network.VPCCidrBlock == "" { + if spec.ControlPlane.Managed && network.VPCCidrBlock == "" { return errors.New("\"vpc_cidr\": is required when \"vpc_id\" is set") } } else { @@ -234,7 +259,7 @@ func validateAzureNetwork(network commons.Networks, managed bool) error { return errors.New("\"vpc_id\": is required when \"subnets\" is set") } if network.VPCCidrBlock != "" { - if managed { + if spec.ControlPlane.Managed { return errors.New("\"vpc_id\": is required when \"vpc_cidr\" is set") } else { return errors.New("\"vpc_cidr\": is only supported in azure managed clusters") @@ -242,11 +267,18 @@ func validateAzureNetwork(network commons.Networks, managed bool) error { } } if len(network.Subnets) > 0 { + subnets, err := getAzureSubnets(creds, subscription, rg, network.VPCID) + if err != nil { + return err + } for _, s := range network.Subnets { if s.SubnetId == "" { return errors.New("\"subnet_id\": is required") } - if managed { + if len(subnets) > 0 && !commons.Contains(subnets, s.SubnetId) { + return errors.New("\"subnet_id\": " + s.SubnetId + " does not belong to VPC: " + network.VPCID + " and resourceGroup: " + rg) + } + if spec.ControlPlane.Managed { if s.CidrBlock == "" { return errors.New("\"cidr\": is required") } @@ -280,7 +312,9 @@ func validateAKSVersion(spec commons.Spec, creds *azidentity.ClientSecretCredent for _, v := range res.KubernetesVersionListResult.Values { for _, p := range v.PatchVersions { for _, u := range p.Upgrades { - availableVersions = append(availableVersions, *u) + if !commons.Contains(availableVersions, *u) { + availableVersions = append(availableVersions, *u) + } } } } @@ -307,3 +341,72 @@ func validateAKSNodes(wn commons.WorkerNodes) error { func hasAzurePremiumStorage(s string) bool { return strings.Contains(strings.ToLower(strings.ReplaceAll(s, "Standard_", "")), "s") } + +func getAzureAzs(creds *azidentity.ClientSecretCredential, subscription string, region string) ([]string, error) { + azs := []string{} + + ctx := context.Background() + clientFactory, err := armsubscriptions.NewClientFactory(creds, nil) + if err != nil { + return []string{}, err + } + pager := clientFactory.NewClient().NewListLocationsPager(subscription, &armsubscriptions.ClientListLocationsOptions{IncludeExtendedLocations: nil}) + for pager.More() { + page, err := pager.NextPage(ctx) + if err != nil { + return []string{}, err + } + for _, v := range page.Value { + if *v.Name == region { + for _, az := range v.AvailabilityZoneMappings { + azs = append(azs, *az.LogicalZone) + } + break + } + } + } + + return azs, nil +} + +func getAzureVpcs(creds *azidentity.ClientSecretCredential, subscription string, region string, resourceGroup string) ([]string, error) { + ctx := context.Background() + vpcs := []string{} + clientFactory, err := armnetwork.NewClientFactory(subscription, creds, nil) + if err != nil { + return []string{}, err + } + pager := clientFactory.NewVirtualNetworksClient().NewListPager(resourceGroup, nil) + for pager.More() { + page, err := pager.NextPage(ctx) + if err != nil { + return []string{}, err + } + for _, v := range page.Value { + if *v.Location == region { + vpcs = append(vpcs, *v.Name) + } + } + } + return vpcs, nil +} + +func getAzureSubnets(creds *azidentity.ClientSecretCredential, subscription string, resourceGroup string, vpcId string) ([]string, error) { + ctx := context.Background() + subnets := []string{} + clientFactory, err := armnetwork.NewClientFactory(subscription, creds, nil) + if err != nil { + return []string{}, err + } + pager := clientFactory.NewSubnetsClient().NewListPager(resourceGroup, vpcId, nil) + for pager.More() { + page, err := pager.NextPage(ctx) + if err != nil { + return []string{}, err + } + for _, v := range page.Value { + subnets = append(subnets, *v.Name) + } + } + return subnets, nil +} diff --git a/pkg/cluster/internal/validate/common.go b/pkg/cluster/internal/validate/common.go index a1cf0a9ffb..38708195c4 100644 --- a/pkg/cluster/internal/validate/common.go +++ b/pkg/cluster/internal/validate/common.go @@ -17,11 +17,13 @@ limitations under the License. package validate import ( + "encoding/json" "fmt" "regexp" "strconv" "strings" + "golang.org/x/exp/slices" "sigs.k8s.io/kind/pkg/commons" "sigs.k8s.io/kind/pkg/errors" ) @@ -31,6 +33,8 @@ const ( MinWorkerNodeNameLength = 3 ) +var k8sVersionSupported = []string{"1.24", "1.25", "1.26", "1.27", "1.28"} + func validateCommon(spec commons.Spec) error { var err error if err = validateK8SVersion(spec.K8SVersion); err != nil { @@ -50,6 +54,16 @@ func validateK8SVersion(v string) error { if !isVersion(v) { return errors.New("spec: Invalid value: \"k8s_version\": regex used for validation is '^v\\d.\\d{2}.\\d{1,2}(-gke.\\d{3,4})?$'") } + K8sVersionMM := strings.Split(v, ".") + a, _ := json.Marshal(k8sVersionSupported) + if len(K8sVersionMM) != 3 { + return errors.New("spec: Invalid value: \"k8s_version\":In this version only supports major and minor Kubernetes versions: " + fmt.Sprint(k8sVersionSupported)) + } + k8sVersion := strings.Join(K8sVersionMM[:len(K8sVersionMM)-1], ".") + if !slices.Contains(k8sVersionSupported, strings.ReplaceAll(k8sVersion, "v", "")) { + + return errors.New("spec: Invalid value: \"k8s_version\": In this version only supports major and minor Kubernetes versions:: " + string(a)) + } return nil } diff --git a/pkg/cluster/internal/validate/gcp.go b/pkg/cluster/internal/validate/gcp.go index 54f4d2f774..4fa1634ae8 100644 --- a/pkg/cluster/internal/validate/gcp.go +++ b/pkg/cluster/internal/validate/gcp.go @@ -17,12 +17,19 @@ limitations under the License. package validate import ( + "context" + "encoding/json" "fmt" + "net/url" "reflect" "regexp" "strconv" "strings" + b64 "encoding/base64" + + "google.golang.org/api/compute/v1" + "google.golang.org/api/option" "sigs.k8s.io/kind/pkg/commons" "sigs.k8s.io/kind/pkg/errors" ) @@ -31,10 +38,15 @@ var GCPVolumes = []string{"pd-balanced", "pd-ssd", "pd-standard", "pd-extreme"} var isGCPNodeImage = regexp.MustCompile(`^projects/[\w-]+/global/images/[\w-]+$`).MatchString var GCPNodeImageFormat = "projects/[PROJECT_ID]/global/images/[IMAGE_NAME]" -func validateGCP(spec commons.Spec) error { +func validateGCP(spec commons.Spec, providerSecrets map[string]string) error { var err error var isGKEVersion = regexp.MustCompile(`^v\d.\d{2}.\d{1,2}-gke.\d{3,4}$`).MatchString + credentialsJson := getGCPCreds(providerSecrets) + azs, err := getGoogleAZs(credentialsJson, spec.Region) + if err != nil { + return err + } if (spec.StorageClass != commons.StorageClass{}) { if err = validateGCPStorageClass(spec); err != nil { return errors.Wrap(err, "spec.storageclass: Invalid value") @@ -42,7 +54,7 @@ func validateGCP(spec commons.Spec) error { } if !reflect.ValueOf(spec.Networks).IsZero() { - if err = validateGCPNetwork(spec.Networks); err != nil { + if err = validateGCPNetwork(spec.Networks, credentialsJson, spec.Region); err != nil { return errors.Wrap(err, "spec.networks: Invalid value") } } @@ -87,6 +99,16 @@ func validateGCP(spec commons.Spec) error { } } + for _, wn := range spec.WorkerNodes { + if wn.AZ != "" { + if len(azs) > 0 { + if !commons.Contains(azs, wn.AZ) { + return errors.New(wn.AZ + " does not exist in this region, azs: " + fmt.Sprint(azs)) + } + } + } + } + return nil } @@ -161,10 +183,21 @@ func validateGCPStorageClass(spec commons.Spec) error { return nil } -func validateGCPNetwork(network commons.Networks) error { +func validateGCPNetwork(network commons.Networks, credentialsJson string, region string) error { if network.VPCID != "" { - if len(network.Subnets) == 0 { - return errors.New("\"subnets\": are required when \"vpc_id\" is set") + vpcs, _ := getGoogleVPCs(credentialsJson) + if len(vpcs) > 0 && !commons.Contains(vpcs, network.VPCID) { + return errors.New("\"vpc_id\": " + network.VPCID + " does not exist") + } + if len(network.Subnets) != 1 { + return errors.New("\"subnet\": when \"vpc_id\" is set, one subnet must be specified") + } + if network.Subnets[0].SubnetId == "" { + return errors.New("\"subnet_id\": required") + } + subnets, _ := getGoogleSubnets(credentialsJson, region, network.VPCID) + if !commons.Contains(subnets, network.Subnets[0].SubnetId) { + return errors.New("\"subnets\": " + network.Subnets[0].SubnetId + " does not belong to vpc with id: " + network.VPCID) } } else { if len(network.Subnets) > 0 { @@ -181,3 +214,115 @@ func validateGCPNetwork(network commons.Networks) error { } return nil } + +func getGoogleVPCs(credentialsJson string) ([]string, error) { + var network_names []string + var ctx = context.Background() + + gcpCreds := map[string]string{} + err := json.Unmarshal([]byte(credentialsJson), &gcpCreds) + if err != nil { + return []string{}, err + } + + cfg := option.WithCredentialsJSON([]byte(credentialsJson)) + computeService, err := compute.NewService(ctx, cfg) + + if err != nil { + return []string{}, err + } + + networks, err := computeService.Networks.List(string(gcpCreds["project_id"])).Do() + if err != nil { + return []string{}, err + } + + for _, network := range networks.Items { + network_names = append(network_names, network.Name) + } + + return network_names, nil + +} + +func getGoogleSubnets(credentialsJson string, region string, vpcId string) ([]string, error) { + var subnetwork_names []string + var ctx = context.Background() + + gcpCreds := map[string]string{} + err := json.Unmarshal([]byte(credentialsJson), &gcpCreds) + if err != nil { + return []string{}, err + } + + cfg := option.WithCredentialsJSON([]byte(credentialsJson)) + computeService, err := compute.NewService(ctx, cfg) + + if err != nil { + return []string{}, err + } + + subnetworks, err := computeService.Subnetworks.List(string(gcpCreds["project_id"]), region).Do() + if err != nil { + return []string{}, err + } + + for _, subnetwork := range subnetworks.Items { + networkParts := strings.Split(subnetwork.Network, "/") + networkId := networkParts[len(networkParts)-1] + if networkId == vpcId { + subnetwork_names = append(subnetwork_names, subnetwork.Name) + } + } + + return subnetwork_names, nil + +} + +func getGoogleAZs(credentialsJson string, region string) ([]string, error) { + var zones_names []string + var ctx = context.Background() + + gcpCreds := map[string]string{} + err := json.Unmarshal([]byte(credentialsJson), &gcpCreds) + if err != nil { + return []string{}, err + } + + cfg := option.WithCredentialsJSON([]byte(credentialsJson)) + computeService, err := compute.NewService(ctx, cfg) + + if err != nil { + return []string{}, err + } + + zones, err := computeService.Zones.List(string(gcpCreds["project_id"])).Filter("name=" + region + "*").Do() + if err != nil { + return []string{}, err + } + + for _, zone := range zones.Items { + zones_names = append(zones_names, zone.Name) + } + + return zones_names, nil +} + +func getGCPCreds(providerSecrets map[string]string) string { + data := map[string]interface{}{ + "type": "service_account", + "project_id": providerSecrets["ProjectID"], + "private_key_id": providerSecrets["PrivateKeyID"], + "private_key": providerSecrets["PrivateKey"], + "client_email": providerSecrets["ClientEmail"], + "client_id": providerSecrets["ClientID"], + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://accounts.google.com/o/oauth2/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/" + url.QueryEscape(providerSecrets["ClientEmail"]), + } + jsonData, _ := json.Marshal(data) + credentials := b64.StdEncoding.EncodeToString([]byte(jsonData)) + credentialsJson, _ := b64.StdEncoding.DecodeString(credentials) + return string(credentialsJson) +} diff --git a/pkg/cluster/internal/validate/validate.go b/pkg/cluster/internal/validate/validate.go index fa0f4f6472..135438a0d1 100644 --- a/pkg/cluster/internal/validate/validate.go +++ b/pkg/cluster/internal/validate/validate.go @@ -43,9 +43,9 @@ func Cluster(params *ValidateParams) (commons.ClusterCredentials, error) { case "aws": err = validateAWS(params.KeosCluster.Spec, creds.ProviderCredentials) case "gcp": - err = validateGCP(params.KeosCluster.Spec) + err = validateGCP(params.KeosCluster.Spec, creds.ProviderCredentials) case "azure": - err = validateAzure(params.KeosCluster.Spec, creds.ProviderCredentials) + err = validateAzure(params.KeosCluster.Spec, creds.ProviderCredentials, params.KeosCluster.Metadata.Name) } if err != nil { return commons.ClusterCredentials{}, err diff --git a/pkg/cmd/kind/version/version.go b/pkg/cmd/kind/version/version.go index b50abfd985..b90134a49b 100644 --- a/pkg/cmd/kind/version/version.go +++ b/pkg/cmd/kind/version/version.go @@ -54,6 +54,7 @@ func DisplayVersion() string { } // versionCore is the core portion of the kind CLI version per Semantic Versioning 2.0.0 + const versionCore = "0.17.0-0.4.0" // versionPreRelease is the base pre-release portion of the kind CLI version per From b87b07101cc227a17f7575dee2a9736305ec321c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Alberto=20Novoa=20Rojas?= <112587171+iamjanr@users.noreply.github.com> Date: Wed, 18 Oct 2023 09:21:33 +0200 Subject: [PATCH 2/6] Add stratio-docs Stratio/document (#342) --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 05f31226bf..cf44abf8d2 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,2 +1,2 @@ * @stg-0 - +/stratio-docs/ @Stratio/document @stg-0 \ No newline at end of file From 00081c41934ff5f969b5921122dc51fcd2fa5e06 Mon Sep 17 00:00:00 2001 From: esierra-stratio <102975650+esierra-stratio@users.noreply.github.com> Date: Wed, 18 Oct 2023 16:32:13 +0200 Subject: [PATCH 3/6] =?UTF-8?q?[CLOUD-53]=20Solicitar=20una=20s=C3=B3la=20?= =?UTF-8?q?vez=20+=20reintentos=20la=20passphrase=20para=20el=20descifrado?= =?UTF-8?q?=20(#344)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * version v0.18.0-alpha * chore: Request the Vault password only once if the secret file exists. * revert change --------- Co-authored-by: Benjamin Elder --- pkg/cmd/kind/create/cluster/createcluster.go | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/pkg/cmd/kind/create/cluster/createcluster.go b/pkg/cmd/kind/create/cluster/createcluster.go index c7114c1d77..6489a41158 100644 --- a/pkg/cmd/kind/create/cluster/createcluster.go +++ b/pkg/cmd/kind/create/cluster/createcluster.go @@ -21,6 +21,7 @@ import ( "fmt" "io" "io/ioutil" + "os" "syscall" "time" @@ -161,7 +162,7 @@ func runE(logger log.Logger, streams cmd.IOStreams, flags *flagpole) error { } if flags.VaultPassword == "" { - flags.VaultPassword, err = setPassword() + flags.VaultPassword, err = setPassword(secretsDefaultPath) if err != nil { return err } @@ -238,17 +239,20 @@ func configOption(rawConfigFlag string, stdin io.Reader) (cluster.CreateOption, return cluster.CreateWithRawConfig(raw), nil } -func setPassword() (string, error) { +func setPassword(secretsDefaultPath string) (string, error) { firstPassword, err := requestPassword("Vault Password: ") if err != nil { return "", err } - secondPassword, err := requestPassword("Rewrite Vault Password:") - if err != nil { - return "", err - } - if firstPassword != secondPassword { - return "", errors.New("The passwords do not match.") + + if _, err := os.Stat(secretsDefaultPath); os.IsNotExist(err) { + secondPassword, err := requestPassword("Rewrite Vault Password:") + if err != nil { + return "", err + } + if firstPassword != secondPassword { + return "", errors.New("The passwords do not match.") + } } return firstPassword, nil From 26633b5241356e43a6347c5de7856cec40ee54ad Mon Sep 17 00:00:00 2001 From: esierra-stratio <102975650+esierra-stratio@users.noreply.github.com> Date: Thu, 19 Oct 2023 13:46:18 +0200 Subject: [PATCH 4/6] chore: validate region exists (#337) * chore: validate region * rename func validate aws * change name function --- pkg/cluster/internal/validate/aws.go | 43 ++++++++++++++++++++++---- pkg/cluster/internal/validate/azure.go | 34 ++++++++++++++++++++ pkg/cluster/internal/validate/gcp.go | 41 ++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 6 deletions(-) diff --git a/pkg/cluster/internal/validate/aws.go b/pkg/cluster/internal/validate/aws.go index dace24d601..ad126da79a 100644 --- a/pkg/cluster/internal/validate/aws.go +++ b/pkg/cluster/internal/validate/aws.go @@ -52,7 +52,15 @@ func validateAWS(spec commons.Spec, providerSecrets map[string]string) error { return err } - azs, err := getAwsAzs(ctx, cfg, spec.Region) + regions, err := getAWSRegions(cfg) + if err != nil { + return err + } + if !commons.Contains(regions, spec.Region) { + return errors.New("spec.region: " + spec.Region + " region does not exist") + } + + azs, err := getAWSAzs(ctx, cfg, spec.Region) if err != nil { return err } @@ -152,14 +160,14 @@ func validateAWSNetwork(ctx context.Context, cfg aws.Config, spec commons.Spec) } } if spec.Networks.VPCID != "" { - vpcs, _ := getAwsVPCs(cfg) + vpcs, _ := getAWSVPCs(cfg) if len(vpcs) > 0 && !commons.Contains(vpcs, spec.Networks.VPCID) { return errors.New("\"vpc_id\": " + spec.Networks.VPCID + " does not exist") } if len(spec.Networks.Subnets) == 0 { return errors.New("\"subnets\": are required when \"vpc_id\" is set") } else { - subnets, _ := getAwsSubnets(spec.Networks.VPCID, cfg) + subnets, _ := getAWSSubnets(spec.Networks.VPCID, cfg) if len(subnets) > 0 { for _, subnet := range spec.Networks.Subnets { if !commons.Contains(subnets, subnet.SubnetId) { @@ -218,7 +226,30 @@ func validateAWSPodsNetwork(podsNetwork string) error { return nil } -func getAwsVPCs(config aws.Config) ([]string, error) { +func getAWSRegions(config aws.Config) ([]string, error) { + regions := []string{} + + // Use a default region to authenticate + config.Region = *aws.String("eu-west-1") + + client := ec2.NewFromConfig(config) + + // Describe regions + describeRegionsOpts := &ec2.DescribeRegionsInput{} + output, err := client.DescribeRegions(context.Background(), describeRegionsOpts) + if err != nil { + return nil, err + } + + // Extract region names + for _, region := range output.Regions { + regions = append(regions, *region.RegionName) + } + + return regions, nil +} + +func getAWSVPCs(config aws.Config) ([]string, error) { vpcs := []string{} client := ec2.NewFromConfig(config) @@ -233,7 +264,7 @@ func getAwsVPCs(config aws.Config) ([]string, error) { return vpcs, nil } -func getAwsSubnets(vpcId string, config aws.Config) ([]string, error) { +func getAWSSubnets(vpcId string, config aws.Config) ([]string, error) { subnets := []string{} client := ec2.NewFromConfig(config) @@ -374,7 +405,7 @@ func validateAWSAZs(ctx context.Context, cfg aws.Config, spec commons.Spec) erro return nil } -func getAwsAzs(ctx context.Context, cfg aws.Config, region string) ([]string, error) { +func getAWSAzs(ctx context.Context, cfg aws.Config, region string) ([]string, error) { var azs []string svc := ec2.NewFromConfig(cfg) result, err := svc.DescribeAvailabilityZones(ctx, &ec2.DescribeAvailabilityZonesInput{}) diff --git a/pkg/cluster/internal/validate/azure.go b/pkg/cluster/internal/validate/azure.go index 8c1a240f2c..831b68ea79 100644 --- a/pkg/cluster/internal/validate/azure.go +++ b/pkg/cluster/internal/validate/azure.go @@ -53,6 +53,16 @@ func validateAzure(spec commons.Spec, providerSecrets map[string]string, cluster if err != nil { return err } + + regions, err := getAzureRegions(creds, providerSecrets["SubscriptionID"]) + fmt.Println(regions) + if err != nil { + return err + } + if !commons.Contains(regions, spec.Region) { + return errors.New("spec.region: " + spec.Region + " region does not exist") + } + azs, err := getAzureAzs(creds, providerSecrets["SubscriptionID"], spec.Region) if err != nil { return err @@ -369,6 +379,30 @@ func getAzureAzs(creds *azidentity.ClientSecretCredential, subscription string, return azs, nil } +func getAzureRegions(creds *azidentity.ClientSecretCredential, subscription string) ([]string, error) { + regions := []string{} + + ctx := context.Background() + clientFactory, err := armsubscriptions.NewClientFactory(creds, nil) + if err != nil { + return []string{}, err + } + + pager := clientFactory.NewClient().NewListLocationsPager(subscription, &armsubscriptions.ClientListLocationsOptions{IncludeExtendedLocations: nil}) + for pager.More() { + page, err := pager.NextPage(ctx) + if err != nil { + return []string{}, err + } + for _, v := range page.Value { + if !commons.Contains(regions, *v.Name) { + regions = append(regions, *v.Name) + } + } + } + return regions, nil +} + func getAzureVpcs(creds *azidentity.ClientSecretCredential, subscription string, region string, resourceGroup string) ([]string, error) { ctx := context.Background() vpcs := []string{} diff --git a/pkg/cluster/internal/validate/gcp.go b/pkg/cluster/internal/validate/gcp.go index 4fa1634ae8..8ae38db3f9 100644 --- a/pkg/cluster/internal/validate/gcp.go +++ b/pkg/cluster/internal/validate/gcp.go @@ -43,6 +43,15 @@ func validateGCP(spec commons.Spec, providerSecrets map[string]string) error { var isGKEVersion = regexp.MustCompile(`^v\d.\d{2}.\d{1,2}-gke.\d{3,4}$`).MatchString credentialsJson := getGCPCreds(providerSecrets) + + regions, err := getGCPRegions(credentialsJson) + if err != nil { + return err + } + if !commons.Contains(regions, spec.Region) { + return errors.New("spec.region: " + spec.Region + " region does not exist") + } + azs, err := getGoogleAZs(credentialsJson, spec.Region) if err != nil { return err @@ -215,6 +224,38 @@ func validateGCPNetwork(network commons.Networks, credentialsJson string, region return nil } +func getGCPRegions(credentialsJson string) ([]string, error) { + var regions_names []string + var ctx = context.Background() + + gcpCreds := map[string]string{} + err := json.Unmarshal([]byte(credentialsJson), &gcpCreds) + if err != nil { + return []string{}, err + } + + cfg := option.WithCredentialsJSON([]byte(credentialsJson)) + computeService, err := compute.NewService(ctx, cfg) + + if err != nil { + return []string{}, err + } + + regions, err := computeService.Regions.List(string(gcpCreds["project_id"])).Do() + if err != nil { + return []string{}, err + } + + for _, region := range regions.Items { + if !commons.Contains(regions_names, region.Name) { + regions_names = append(regions_names, region.Name) + } + } + + return regions_names, nil + +} + func getGoogleVPCs(credentialsJson string) ([]string, error) { var network_names []string var ctx = context.Background() From 413afa18df72c0037d2c6fc4b7d10937c4f0f913 Mon Sep 17 00:00:00 2001 From: Francisco Augusto Date: Tue, 24 Oct 2023 11:03:32 +0200 Subject: [PATCH 5/6] Remove println (#347) --- pkg/cluster/internal/validate/azure.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/cluster/internal/validate/azure.go b/pkg/cluster/internal/validate/azure.go index 831b68ea79..0b25f9f30d 100644 --- a/pkg/cluster/internal/validate/azure.go +++ b/pkg/cluster/internal/validate/azure.go @@ -55,7 +55,6 @@ func validateAzure(spec commons.Spec, providerSecrets map[string]string, cluster } regions, err := getAzureRegions(creds, providerSecrets["SubscriptionID"]) - fmt.Println(regions) if err != nil { return err } From 243464c0a3ab171bfc8aa7dfd02e92e0f6204011 Mon Sep 17 00:00:00 2001 From: Francisco Augusto Date: Tue, 24 Oct 2023 11:48:29 +0200 Subject: [PATCH 6/6] Fix k8s version common validation for gke (#348) --- pkg/cluster/internal/validate/common.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/pkg/cluster/internal/validate/common.go b/pkg/cluster/internal/validate/common.go index 38708195c4..63644b902a 100644 --- a/pkg/cluster/internal/validate/common.go +++ b/pkg/cluster/internal/validate/common.go @@ -17,7 +17,6 @@ limitations under the License. package validate import ( - "encoding/json" "fmt" "regexp" "strconv" @@ -55,14 +54,9 @@ func validateK8SVersion(v string) error { return errors.New("spec: Invalid value: \"k8s_version\": regex used for validation is '^v\\d.\\d{2}.\\d{1,2}(-gke.\\d{3,4})?$'") } K8sVersionMM := strings.Split(v, ".") - a, _ := json.Marshal(k8sVersionSupported) - if len(K8sVersionMM) != 3 { - return errors.New("spec: Invalid value: \"k8s_version\":In this version only supports major and minor Kubernetes versions: " + fmt.Sprint(k8sVersionSupported)) - } - k8sVersion := strings.Join(K8sVersionMM[:len(K8sVersionMM)-1], ".") + k8sVersion := strings.Join(K8sVersionMM[:2], ".") if !slices.Contains(k8sVersionSupported, strings.ReplaceAll(k8sVersion, "v", "")) { - - return errors.New("spec: Invalid value: \"k8s_version\": In this version only supports major and minor Kubernetes versions:: " + string(a)) + return errors.New("spec: Invalid value: \"k8s_version\": kubernetes versions supported: " + fmt.Sprint(strings.Join(k8sVersionSupported, ", "))) } return nil }