Skip to content

Commit

Permalink
Support master authorized network config (kubernetes-sigs#65)
Browse files Browse the repository at this point in the history
* add MasterAuthorizedNetworksConfig filed into GCPManagedControlPlane CRD

* fix

* disable it if no desired specified in CR

* change to do not reconcile if the desired CR is nil

* add comment

add nil handling fo GcpPublicCidrsAccessEnabled

change nil as enabled

fix display_name not to be optional

address comments

fix typo and remove empty line
  • Loading branch information
yuecong authored and richardchen-db committed Oct 5, 2023
1 parent 2131458 commit 76c3205
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 4 deletions.
87 changes: 83 additions & 4 deletions cloud/services/container/clusters/reconcile.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

"cloud.google.com/go/container/apiv1/containerpb"
"github.com/go-logr/logr"
"github.com/google/go-cmp/cmp"
"github.com/googleapis/gax-go/v2/apierror"
"github.com/pkg/errors"
"google.golang.org/grpc/codes"
Expand Down Expand Up @@ -136,7 +137,7 @@ func (s *Service) Reconcile(ctx context.Context) (ctrl.Result, error) {
return ctrl.Result{}, statusErr
}

needUpdate, updateClusterRequest := s.checkDiffAndPrepareUpdate(cluster)
needUpdate, updateClusterRequest := s.checkDiffAndPrepareUpdate(cluster, &log)
if needUpdate {
log.Info("Update required")
err = s.updateCluster(ctx, updateClusterRequest, &log)
Expand Down Expand Up @@ -258,6 +259,7 @@ func (s *Service) createCluster(ctx context.Context, log *logr.Logger) error {
ReleaseChannel: &containerpb.ReleaseChannel{
Channel: convertToSdkReleaseChannel(s.scope.GCPManagedControlPlane.Spec.ReleaseChannel),
},
MasterAuthorizedNetworksConfig: convertToSdkMasterAuthorizedNetworksConfig(s.scope.GCPManagedControlPlane.Spec.MasterAuthorizedNetworksConfig),
}
if s.scope.GCPManagedControlPlane.Spec.ControlPlaneVersion != nil {
cluster.InitialClusterVersion = *s.scope.GCPManagedControlPlane.Spec.ControlPlaneVersion
Expand Down Expand Up @@ -320,25 +322,102 @@ func convertToSdkReleaseChannel(channel *infrav1exp.ReleaseChannel) containerpb.
}
}

func (s *Service) checkDiffAndPrepareUpdate(existingCluster *containerpb.Cluster) (bool, *containerpb.UpdateClusterRequest) {
// convertToSdkMasterAuthorizedNetworksConfig converts the MasterAuthorizedNetworksConfig defined in CRs to the SDK version.
func convertToSdkMasterAuthorizedNetworksConfig(config *infrav1exp.MasterAuthorizedNetworksConfig) *containerpb.MasterAuthorizedNetworksConfig {
// if config is nil, it means that the user wants to disable the feature.
if config == nil {
return &containerpb.MasterAuthorizedNetworksConfig{
Enabled: false,
CidrBlocks: []*containerpb.MasterAuthorizedNetworksConfig_CidrBlock{},
GcpPublicCidrsAccessEnabled: new(bool),
}
}

// Convert the CidrBlocks slice.
cidrBlocks := make([]*containerpb.MasterAuthorizedNetworksConfig_CidrBlock, len(config.CidrBlocks))
for i, cidrBlock := range config.CidrBlocks {
cidrBlocks[i] = &containerpb.MasterAuthorizedNetworksConfig_CidrBlock{
CidrBlock: cidrBlock.CidrBlock,
DisplayName: cidrBlock.DisplayName,
}
}

return &containerpb.MasterAuthorizedNetworksConfig{
Enabled: true,
CidrBlocks: cidrBlocks,
GcpPublicCidrsAccessEnabled: config.GcpPublicCidrsAccessEnabled,
}
}

func (s *Service) checkDiffAndPrepareUpdate(existingCluster *containerpb.Cluster, log *logr.Logger) (bool, *containerpb.UpdateClusterRequest) {
log.V(4).Info("Checking diff and preparing update.")

needUpdate := false
clusterUpdate := containerpb.ClusterUpdate{}
// Release channel
desiredReleaseChannel := convertToSdkReleaseChannel(s.scope.GCPManagedControlPlane.Spec.ReleaseChannel)
if desiredReleaseChannel != existingCluster.ReleaseChannel.Channel {
log.V(2).Info("Release channel update required", "current", existingCluster.ReleaseChannel.Channel, "desired", desiredReleaseChannel)
needUpdate = true
clusterUpdate.DesiredReleaseChannel = &containerpb.ReleaseChannel{
Channel: desiredReleaseChannel,
}
}
// Master version
if s.scope.GCPManagedControlPlane.Spec.ControlPlaneVersion != nil && *s.scope.GCPManagedControlPlane.Spec.ControlPlaneVersion != existingCluster.InitialClusterVersion {
if s.scope.GCPManagedControlPlane.Spec.ControlPlaneVersion != nil {
desiredMasterVersion := *s.scope.GCPManagedControlPlane.Spec.ControlPlaneVersion
if desiredMasterVersion != existingCluster.InitialClusterVersion {
needUpdate = true
clusterUpdate.DesiredMasterVersion = desiredMasterVersion
log.V(2).Info("Master version update required", "current", existingCluster.InitialClusterVersion, "desired", desiredMasterVersion)
}
}

// DesiredMasterAuthorizedNetworksConfig
// When desiredMasterAuthorizedNetworksConfig is nil, it means that the user wants to disable the feature.
desiredMasterAuthorizedNetworksConfig := convertToSdkMasterAuthorizedNetworksConfig(s.scope.GCPManagedControlPlane.Spec.MasterAuthorizedNetworksConfig)
if !compareMasterAuthorizedNetworksConfig(desiredMasterAuthorizedNetworksConfig, existingCluster.MasterAuthorizedNetworksConfig) {
needUpdate = true
clusterUpdate.DesiredMasterVersion = *s.scope.GCPManagedControlPlane.Spec.ControlPlaneVersion
clusterUpdate.DesiredMasterAuthorizedNetworksConfig = desiredMasterAuthorizedNetworksConfig
log.V(2).Info("Master authorized networks config update required", "current", existingCluster.MasterAuthorizedNetworksConfig, "desired", desiredMasterAuthorizedNetworksConfig)
}
log.V(4).Info("Master authorized networks config update check", "current", existingCluster.MasterAuthorizedNetworksConfig)
if desiredMasterAuthorizedNetworksConfig != nil {
log.V(4).Info("Master authorized networks config update check", "desired", desiredMasterAuthorizedNetworksConfig)
}

updateClusterRequest := containerpb.UpdateClusterRequest{
Name: s.scope.ClusterFullName(),
Update: &clusterUpdate,
}
log.V(4).Info("Update cluster request. ", "needUpdate", needUpdate, "updateClusterRequest", &updateClusterRequest)
return needUpdate, &updateClusterRequest
}

// compare if two MasterAuthorizedNetworksConfig are equal.
func compareMasterAuthorizedNetworksConfig(a, b *containerpb.MasterAuthorizedNetworksConfig) bool {
if a == nil && b == nil {
return true
}
if a == nil || b == nil {
return false
}

if a.Enabled != b.Enabled {
return false
}
if (a.GcpPublicCidrsAccessEnabled == nil && b.GcpPublicCidrsAccessEnabled != nil) || (a.GcpPublicCidrsAccessEnabled != nil && b.GcpPublicCidrsAccessEnabled == nil) {
return false
}
if a.GcpPublicCidrsAccessEnabled != nil && b.GcpPublicCidrsAccessEnabled != nil && *a.GcpPublicCidrsAccessEnabled != *b.GcpPublicCidrsAccessEnabled {
return false
}
// if one cidrBlocks is nil, but the other is empty, they are equal.
if (a.CidrBlocks == nil && b.CidrBlocks != nil && len(b.CidrBlocks) == 0) || (b.CidrBlocks == nil && a.CidrBlocks != nil && len(a.CidrBlocks) == 0) {
return true
}
if !cmp.Equal(a.CidrBlocks, b.CidrBlocks) {
return false
}
return true
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,33 @@ spec:
description: Location represents the location (region or zone) in
which the GKE cluster will be created.
type: string
master_authorized_networks_config:
description: MasterAuthorizedNetworksConfig represents configuration
options for master authorized networks feature of the GKE cluster.
This feature is disabled if this field is not specified.
properties:
cidr_blocks:
description: cidr_blocks define up to 50 external networks that
could access Kubernetes master through HTTPS.
items:
description: MasterAuthorizedNetworksConfigCidrBlock contains
an optional name and one CIDR block.
properties:
cidr_block:
description: cidr_block must be specified in CIDR notation.
pattern: ^(?:[0-9]{1,3}\.){3}[0-9]{1,3}(?:\/([0-9]|[1-2][0-9]|3[0-2]))?$|^([a-fA-F0-9:]+:+)+[a-fA-F0-9]+\/[0-9]{1,3}$
type: string
display_name:
description: display_name is an field for users to identify
CIDR blocks.
type: string
type: object
type: array
gcp_public_cidrs_access_enabled:
description: Whether master is accessible via Google Compute Engine
Public IP addresses.
type: boolean
type: object
project:
description: Project is the name of the project to deploy the cluster
to.
Expand Down
27 changes: 27 additions & 0 deletions exp/api/v1beta1/gcpmanagedcontrolplane_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ type GCPManagedControlPlaneSpec struct {
// Endpoint represents the endpoint used to communicate with the control plane.
// +optional
Endpoint clusterv1.APIEndpoint `json:"endpoint"`
// MasterAuthorizedNetworksConfig represents configuration options for master authorized networks feature of the GKE cluster.
// This feature is disabled if this field is not specified.
// +optional
MasterAuthorizedNetworksConfig *MasterAuthorizedNetworksConfig `json:"master_authorized_networks_config,omitempty"`
}

// GCPManagedControlPlaneStatus defines the observed state of GCPManagedControlPlane.
Expand Down Expand Up @@ -115,6 +119,29 @@ const (
Stable ReleaseChannel = "stable"
)

// MasterAuthorizedNetworksConfig contains configuration options for the master authorized networks feature.
// Enabled master authorized networks will disallow all external traffic to access
// Kubernetes master through HTTPS except traffic from the given CIDR blocks,
// Google Compute Engine Public IPs and Google Prod IPs.
type MasterAuthorizedNetworksConfig struct {
// cidr_blocks define up to 50 external networks that could access
// Kubernetes master through HTTPS.
// +optional
CidrBlocks []*MasterAuthorizedNetworksConfigCidrBlock `json:"cidr_blocks,omitempty"`
// Whether master is accessible via Google Compute Engine Public IP addresses.
// +optional
GcpPublicCidrsAccessEnabled *bool `json:"gcp_public_cidrs_access_enabled,omitempty"`
}

// MasterAuthorizedNetworksConfigCidrBlock contains an optional name and one CIDR block.
type MasterAuthorizedNetworksConfigCidrBlock struct {
// display_name is an field for users to identify CIDR blocks.
DisplayName string `json:"display_name,omitempty"`
// cidr_block must be specified in CIDR notation.
// +kubebuilder:validation:Pattern=`^(?:[0-9]{1,3}\.){3}[0-9]{1,3}(?:\/([0-9]|[1-2][0-9]|3[0-2]))?$|^([a-fA-F0-9:]+:+)+[a-fA-F0-9]+\/[0-9]{1,3}$`
CidrBlock string `json:"cidr_block,omitempty"`
}

// GetConditions returns the control planes conditions.
func (r *GCPManagedControlPlane) GetConditions() clusterv1.Conditions {
return r.Status.Conditions
Expand Down
51 changes: 51 additions & 0 deletions exp/api/v1beta1/zz_generated.deepcopy.go

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

0 comments on commit 76c3205

Please sign in to comment.