Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for GKE Autopilot #251

Merged
merged 1 commit into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ MOCKGEN_BIN := mockgen
MOCKGEN := $(BIN_DIR)/$(MOCKGEN_BIN)-$(MOCKGEN_VER)
MOCKGEN_PKG := github.com/golang/mock/mockgen

GINKGO_VER := v2.12.0
GINKGO_VER := v2.13.2
GINKGO_BIN := ginkgo
GINKGO := $(BIN_DIR)/$(GINKGO_BIN)-$(GINKGO_VER)

Expand Down
6 changes: 6 additions & 0 deletions charts/gke-operator-crd/templates/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ spec:
properties:
spec:
properties:
autopilotConfig:
nullable: true
properties:
enabled:
type: boolean
type: object
clusterAddons:
nullable: true
properties:
Expand Down
8 changes: 7 additions & 1 deletion controller/gke-cluster-config-handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ func (h *Handler) updateUpstreamClusterState(config *gkev1.GKEClusterConfig, ups
return h.enqueueUpdate(config)
}

if config.Spec.NodePools != nil {
if config.Spec.NodePools != nil && (config.Spec.AutopilotConfig == nil || !config.Spec.AutopilotConfig.Enabled) {
downstreamNodePools, err := buildNodePoolMap(config.Spec.NodePools, config.Name)
if err != nil {
return config, err
Expand Down Expand Up @@ -636,6 +636,12 @@ func BuildUpstreamClusterState(cluster *gkeapi.Cluster) (*gkev1.GKEClusterConfig
newSpec.MaintenanceWindow = &cluster.MaintenancePolicy.Window.DailyMaintenanceWindow.StartTime
}

if cluster.Autopilot != nil && cluster.Autopilot.Enabled {
newSpec.AutopilotConfig = &gkev1.GKEAutopilotConfig{
Enabled: cluster.Autopilot.Enabled,
}
}

// build node groups
newSpec.NodePools = make([]gkev1.GKENodePoolConfig, 0, len(cluster.NodePools))

Expand Down
34 changes: 34 additions & 0 deletions examples/cluster-autopilot.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
apiVersion: gke.cattle.io/v1
kind: GKEClusterConfig
metadata:
name: example-autopilot-cluster
spec:
clusterName: "example-autopilot-cluster"
description: "Example cluster with autopilot"
labels: {}
region: "us-east1"
projectID: "example-project"
kubernetesVersion: "1.27.3-gke.100"
loggingService: ""
monitoringService: ""
enableKubernetesAlpha: false
clusterIpv4Cidr: "10.43.0.0/16"
ipAllocationPolicy:
useIpAliases: true
clusterAddons:
httpLoadBalancing: true
networkPolicyConfig: false
horizontalPodAutoscaling: true
networkPolicyEnabled: false
network: default
subnetwork: default
privateClusterConfig:
enablePrivateEndpoint: false
enablePrivateNodes: false
masterAuthorizedNetworks:
enabled: false
locations: []
maintenanceWindow: ""
googleCredentialSecret: "cattle-global-data:cc-gqpl4"
autopilotConfig:
enabled: true
9 changes: 9 additions & 0 deletions pkg/apis/gke.cattle.io/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ type GKEClusterConfigSpec struct {
// MaintenanceWindow is the maintenance window configuration.
// +optional
MaintenanceWindow *string `json:"maintenanceWindow,omitempty" norman:"pointer"`

// GKE Autopilot is a mode of operation in GKE in which Google manages your cluster configuration,
// including your nodes, scaling, security, and other preconfigured settings.
// +optional
AutopilotConfig *GKEAutopilotConfig `json:"autopilotConfig,omitempty"`
}

type GKEIPAllocationPolicy struct {
Expand Down Expand Up @@ -350,3 +355,7 @@ type GKENodePoolManagement struct {
// +kubebuilder:default=false
AutoUpgrade bool `json:"autoUpgrade,omitempty"`
}

type GKEAutopilotConfig struct {
Enabled bool `json:"enabled,omitempty"`
}
21 changes: 21 additions & 0 deletions pkg/apis/gke.cattle.io/v1/zz_generated_deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 30 additions & 20 deletions pkg/gke/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,29 +98,35 @@ func newClusterCreateRequest(config *gkev1.GKEClusterConfig) *gkeapi.CreateClust
}
}

addons := config.Spec.ClusterAddons
request.Cluster.AddonsConfig.HttpLoadBalancing = &gkeapi.HttpLoadBalancing{Disabled: !addons.HTTPLoadBalancing}
request.Cluster.AddonsConfig.HorizontalPodAutoscaling = &gkeapi.HorizontalPodAutoscaling{Disabled: !addons.HorizontalPodAutoscaling}
request.Cluster.AddonsConfig.NetworkPolicyConfig = &gkeapi.NetworkPolicyConfig{Disabled: !addons.NetworkPolicyConfig}
if config.Spec.AutopilotConfig != nil && config.Spec.AutopilotConfig.Enabled {
request.Cluster.Autopilot = &gkeapi.Autopilot{
Enabled: config.Spec.AutopilotConfig.Enabled,
}
} else {
addons := config.Spec.ClusterAddons
request.Cluster.AddonsConfig.HttpLoadBalancing = &gkeapi.HttpLoadBalancing{Disabled: !addons.HTTPLoadBalancing}
request.Cluster.AddonsConfig.HorizontalPodAutoscaling = &gkeapi.HorizontalPodAutoscaling{Disabled: !addons.HorizontalPodAutoscaling}
request.Cluster.AddonsConfig.NetworkPolicyConfig = &gkeapi.NetworkPolicyConfig{Disabled: !addons.NetworkPolicyConfig}

request.Cluster.NodePools = make([]*gkeapi.NodePool, 0, len(config.Spec.NodePools))
request.Cluster.NodePools = make([]*gkeapi.NodePool, 0, len(config.Spec.NodePools))

for np := range config.Spec.NodePools {
nodePool := newGKENodePoolFromConfig(&config.Spec.NodePools[np], config)
request.Cluster.NodePools = append(request.Cluster.NodePools, nodePool)
}

if config.Spec.MasterAuthorizedNetworksConfig != nil {
blocks := make([]*gkeapi.CidrBlock, len(config.Spec.MasterAuthorizedNetworksConfig.CidrBlocks))
for _, b := range config.Spec.MasterAuthorizedNetworksConfig.CidrBlocks {
blocks = append(blocks, &gkeapi.CidrBlock{
CidrBlock: b.CidrBlock,
DisplayName: b.DisplayName,
})
for np := range config.Spec.NodePools {
nodePool := newGKENodePoolFromConfig(&config.Spec.NodePools[np], config)
request.Cluster.NodePools = append(request.Cluster.NodePools, nodePool)
}
request.Cluster.MasterAuthorizedNetworksConfig = &gkeapi.MasterAuthorizedNetworksConfig{
Enabled: config.Spec.MasterAuthorizedNetworksConfig.Enabled,
CidrBlocks: blocks,

if config.Spec.MasterAuthorizedNetworksConfig != nil {
blocks := make([]*gkeapi.CidrBlock, len(config.Spec.MasterAuthorizedNetworksConfig.CidrBlocks))
for _, b := range config.Spec.MasterAuthorizedNetworksConfig.CidrBlocks {
blocks = append(blocks, &gkeapi.CidrBlock{
CidrBlock: b.CidrBlock,
DisplayName: b.DisplayName,
})
}
request.Cluster.MasterAuthorizedNetworksConfig = &gkeapi.MasterAuthorizedNetworksConfig{
Enabled: config.Spec.MasterAuthorizedNetworksConfig.Enabled,
CidrBlocks: blocks,
}
}
}

Expand Down Expand Up @@ -163,6 +169,10 @@ func validateCreateRequest(ctx context.Context, gkeClient services.GKEClusterSer
return fmt.Errorf("cluster name is required")
}

if len(config.Spec.NodePools) != 0 && config.Spec.AutopilotConfig != nil && config.Spec.AutopilotConfig.Enabled {
return fmt.Errorf("cannot create node pools for autopilot clusters")
}

nodeP := map[string]bool{}
for _, np := range config.Spec.NodePools {
if np.Name == nil {
Expand Down
67 changes: 67 additions & 0 deletions pkg/gke/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,73 @@ var _ = Describe("CreateCluster", func() {
Expect(err).To(HaveOccurred())
})

It("should successfully create autopilot cluster", func() {
config.Spec.ClusterName = "test-autopilot-cluster"
config.Spec.AutopilotConfig = &gkev1.GKEAutopilotConfig{
Enabled: true,
}

createClusterRequest := newClusterCreateRequest(config)
clusterServiceMock.EXPECT().
ClusterCreate(
ctx,
LocationRRN(config.Spec.ProjectID, Location(config.Spec.Region, config.Spec.Zone)),
createClusterRequest).
Return(&gkeapi.Operation{}, nil)

clusterServiceMock.EXPECT().
ClusterList(
ctx,
LocationRRN(config.Spec.ProjectID, Location(config.Spec.Region, config.Spec.Zone))).
Return(&gkeapi.ListClustersResponse{}, nil)

err := Create(ctx, clusterServiceMock, config)
Expect(err).ToNot(HaveOccurred())

clusterServiceMock.EXPECT().
ClusterGet(
ctx,
ClusterRRN(config.Spec.ProjectID, Location(config.Spec.Region, config.Spec.Zone),
config.Spec.ClusterName)).
Return(
&gkeapi.Cluster{
Name: "test-autopilot-cluster",
}, nil)

managedCluster, err := GetCluster(ctx, clusterServiceMock, &config.Spec)
Expect(err).ToNot(HaveOccurred())
Expect(managedCluster.Name).To(Equal(config.Spec.ClusterName))
})

It("should fail to create autopilot cluster with nodepools", func() {
config.Spec.ClusterName = "test-autopilot-cluster"
config.Spec.AutopilotConfig = &gkev1.GKEAutopilotConfig{
Enabled: true,
}

config.Spec.NodePools = []gkev1.GKENodePoolConfig{
{
Name: &nodePoolName,
InitialNodeCount: &initialNodeCount,
Version: &k8sVersion,
MaxPodsConstraint: &maxPodsConstraint,
Config: &gkev1.GKENodeConfig{},
Autoscaling: &gkev1.GKENodePoolAutoscaling{
Enabled: true,
MinNodeCount: 3,
MaxNodeCount: 5,
},
Management: &gkev1.GKENodePoolManagement{
AutoRepair: true,
AutoUpgrade: true,
},
},
}

err := Create(ctx, clusterServiceMock, config)
Expect(err).To(HaveOccurred())
})

It("should fail to create cluster with duplicated nodepool names", func() {
config.Spec.NodePools = []gkev1.GKENodePoolConfig{
{
Expand Down
Loading