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

feat: per cluster credentials #775

Merged
merged 1 commit into from
Dec 7, 2022
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
4 changes: 4 additions & 0 deletions api/v1alpha3/gcpcluster_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ func (src *GCPCluster) ConvertTo(dstRaw conversion.Hub) error { // nolint
}
}

if restored.Spec.CredentialsRef != nil {
dst.Spec.CredentialsRef = restored.Spec.CredentialsRef
}

return nil
}

Expand Down
1 change: 1 addition & 0 deletions api/v1alpha3/zz_generated.conversion.go

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

16 changes: 8 additions & 8 deletions api/v1alpha4/conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,22 @@ import (

func TestFuzzyConversion(t *testing.T) {
t.Run("for GCPCluster", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{
Hub: &v1beta1.GCPCluster{},
Spoke: &GCPCluster{},
Hub: &v1beta1.GCPCluster{},
Spoke: &GCPCluster{},
}))

t.Run("for GCPClusterTemplate", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{
Hub: &v1beta1.GCPClusterTemplate{},
Spoke: &GCPClusterTemplate{},
Hub: &v1beta1.GCPClusterTemplate{},
Spoke: &GCPClusterTemplate{},
}))

t.Run("for GCPMachine", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{
Hub: &v1beta1.GCPMachine{},
Spoke: &GCPMachine{},
Hub: &v1beta1.GCPMachine{},
Spoke: &GCPMachine{},
}))

t.Run("for GCPMachineTemplate", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{
Hub: &v1beta1.GCPMachineTemplate{},
Spoke: &GCPMachineTemplate{},
Hub: &v1beta1.GCPMachineTemplate{},
Spoke: &GCPMachineTemplate{},
}))
}
9 changes: 9 additions & 0 deletions api/v1alpha4/gcpcluster_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package v1alpha4
import (
apiconversion "k8s.io/apimachinery/pkg/conversion"
infrav1beta1 "sigs.k8s.io/cluster-api-provider-gcp/api/v1beta1"
v1beta1 "sigs.k8s.io/cluster-api-provider-gcp/api/v1beta1"
utilconversion "sigs.k8s.io/cluster-api/util/conversion"
"sigs.k8s.io/controller-runtime/pkg/conversion"
)
Expand Down Expand Up @@ -47,6 +48,9 @@ func (src *GCPCluster) ConvertTo(dstRaw conversion.Hub) error { // nolint
break
}
}
if restored.Spec.CredentialsRef != nil {
dst.Spec.CredentialsRef = restored.Spec.CredentialsRef.DeepCopy()
}

return nil
}
Expand Down Expand Up @@ -83,3 +87,8 @@ func (dst *GCPClusterList) ConvertFrom(srcRaw conversion.Hub) error { // nolint
func Convert_v1beta1_SubnetSpec_To_v1alpha4_SubnetSpec(in *infrav1beta1.SubnetSpec, out *SubnetSpec, s apiconversion.Scope) error {
return autoConvert_v1beta1_SubnetSpec_To_v1alpha4_SubnetSpec(in, out, s)
}

// Convert_v1beta1_GCPClusterSpec_To_v1alpha4_GCPClusterSpec is an autogenerated conversion function.
func Convert_v1beta1_GCPClusterSpec_To_v1alpha4_GCPClusterSpec(in *v1beta1.GCPClusterSpec, out *GCPClusterSpec, s apiconversion.Scope) error {
return autoConvert_v1beta1_GCPClusterSpec_To_v1alpha4_GCPClusterSpec(in, out, s)
}
4 changes: 4 additions & 0 deletions api/v1alpha4/gcpclustertemplate_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ func (src *GCPClusterTemplate) ConvertTo(dstRaw conversion.Hub) error { // nolin
}
}

if restored.Spec.Template.Spec.CredentialsRef != nil {
dst.Spec.Template.Spec.CredentialsRef = restored.Spec.Template.Spec.CredentialsRef.DeepCopy()
}

return nil
}

Expand Down
16 changes: 6 additions & 10 deletions api/v1alpha4/zz_generated.conversion.go

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

5 changes: 5 additions & 0 deletions api/v1beta1/gcpcluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ type GCPClusterSpec struct {
// ones added by default.
// +optional
AdditionalLabels Labels `json:"additionalLabels,omitempty"`

// CredentialsRef is a reference to a Secret that contains the credentials to use for provisioning this cluster. If not
// supplied then the credentials of the controller will be used.
// +optional
CredentialsRef *ObjectReference `json:"credentialsRef,omitempty"`
}

// GCPClusterStatus defines the observed state of GCPCluster.
Expand Down
7 changes: 7 additions & 0 deletions api/v1beta1/gcpcluster_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ func (c *GCPCluster) ValidateUpdate(oldRaw runtime.Object) error {
)
}

if !reflect.DeepEqual(c.Spec.CredentialsRef, old.Spec.CredentialsRef) {
allErrs = append(allErrs,
field.Invalid(field.NewPath("spec", "CredentialsRef"),
c.Spec.CredentialsRef, "field is immutable"),
)
}

if len(allErrs) == 0 {
return nil
}
Expand Down
12 changes: 12 additions & 0 deletions api/v1beta1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,3 +255,15 @@ type ServiceAccount struct {
// account.
Scopes []string `json:"scopes,omitempty"`
}

// ObjectReference is a reference to another Kubernetes object instance.
type ObjectReference struct {
// Namespace of the referent.
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
// +kubebuilder:validation:Required
Namespace string `json:"namespace"`
// Name of the referent.
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
// +kubebuilder:validation:Required
Name string `json:"name"`
}
20 changes: 20 additions & 0 deletions api/v1beta1/zz_generated.deepcopy.go

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

40 changes: 38 additions & 2 deletions cloud/scope/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ import (

"github.com/pkg/errors"
"google.golang.org/api/compute/v1"
"google.golang.org/api/option"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/utils/pointer"
infrav1 "sigs.k8s.io/cluster-api-provider-gcp/api/v1beta1"
"sigs.k8s.io/cluster-api-provider-gcp/cloud"
Expand All @@ -42,7 +45,7 @@ type ClusterScopeParams struct {

// NewClusterScope creates a new Scope from the supplied parameters.
// This is meant to be called for each reconcile iteration.
func NewClusterScope(params ClusterScopeParams) (*ClusterScope, error) {
func NewClusterScope(ctx context.Context, params ClusterScopeParams) (*ClusterScope, error) {
if params.Cluster == nil {
return nil, errors.New("failed to generate new scope from nil Cluster")
}
Expand All @@ -51,7 +54,7 @@ func NewClusterScope(params ClusterScopeParams) (*ClusterScope, error) {
}

if params.GCPServices.Compute == nil {
computeSvc, err := compute.NewService(context.TODO())
computeSvc, err := createComputeService(ctx, params)
if err != nil {
return nil, errors.Errorf("failed to create gcp compute client: %v", err)
}
Expand All @@ -73,6 +76,39 @@ func NewClusterScope(params ClusterScopeParams) (*ClusterScope, error) {
}, nil
}

func createComputeService(ctx context.Context, params ClusterScopeParams) (*compute.Service, error) {
if params.GCPCluster.Spec.CredentialsRef == nil {
computeSvc, err := compute.NewService(ctx)
if err != nil {
return nil, errors.Errorf("failed to create gcp compute client: %v", err)
}

return computeSvc, nil
}

secretRefName := types.NamespacedName{
Name: params.GCPCluster.Spec.CredentialsRef.Name,
Namespace: params.GCPCluster.Spec.CredentialsRef.Namespace,
}

credSecret := &corev1.Secret{}
if err := params.Client.Get(ctx, secretRefName, credSecret); err != nil {
return nil, fmt.Errorf("getting credentials secret %s\\%s: %w", secretRefName.Namespace, secretRefName.Name, err)
}

rawData, ok := credSecret.Data["credentials"]
if !ok {
return nil, errors.New("no credentials key in secret")
}

computeSvc, err := compute.NewService(ctx, option.WithCredentialsJSON(rawData))
if err != nil {
return nil, errors.Errorf("failed to create gcp compute client with credentials secret: %v", err)
}

return computeSvc, nil
}

// ClusterScope defines the basic context for an actuator to operate upon.
type ClusterScope struct {
client client.Client
Expand Down
4 changes: 2 additions & 2 deletions cloud/services/compute/instances/reconcile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func TestService_createOrGetInstance(t *testing.T) {
WithObjects(fakeBootstrapSecret).
Build()

clusterScope, err := scope.NewClusterScope(scope.ClusterScopeParams{
clusterScope, err := scope.NewClusterScope(context.TODO(), scope.ClusterScopeParams{
Client: fakec,
Cluster: fakeCluster,
GCPCluster: fakeGCPCluster,
Expand All @@ -155,7 +155,7 @@ func TestService_createOrGetInstance(t *testing.T) {
t.Fatal(err)
}

clusterScopeWithoutFailureDomain, err := scope.NewClusterScope(scope.ClusterScopeParams{
clusterScopeWithoutFailureDomain, err := scope.NewClusterScope(context.TODO(), scope.ClusterScopeParams{
Client: fakec,
Cluster: fakeCluster,
GCPCluster: fakeGCPClusterWithOutFailureDomain,
Expand Down
4 changes: 2 additions & 2 deletions cloud/services/compute/subnets/reconcile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func TestService_Reconcile(t *testing.T) {
WithScheme(scheme.Scheme).
Build()

clusterScope, err := scope.NewClusterScope(scope.ClusterScopeParams{
clusterScope, err := scope.NewClusterScope(context.TODO(), scope.ClusterScopeParams{
Client: fakec,
Cluster: fakeCluster,
GCPCluster: fakeGCPCluster,
Expand Down Expand Up @@ -179,7 +179,7 @@ func TestService_Delete(t *testing.T) {
WithScheme(scheme.Scheme).
Build()

clusterScope, err := scope.NewClusterScope(scope.ClusterScopeParams{
clusterScope, err := scope.NewClusterScope(context.TODO(), scope.ClusterScopeParams{
Client: fakec,
Cluster: fakeCluster,
GCPCluster: fakeGCPCluster,
Expand Down
15 changes: 15 additions & 0 deletions config/crd/bases/infrastructure.cluster.x-k8s.io_gcpclusters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,21 @@ spec:
- host
- port
type: object
credentialsRef:
description: CredentialsRef is a reference to a Secret that contains
the credentials to use for provisioning this cluster. If not supplied
then the credentials of the controller will be used.
properties:
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
type: string
namespace:
description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
type: string
required:
- name
- namespace
type: object
failureDomains:
description: FailureDomains is an optional field which is used to
assign selected availability zones to a cluster FailureDomains if
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,22 @@ spec:
- host
- port
type: object
credentialsRef:
description: CredentialsRef is a reference to a Secret that
contains the credentials to use for provisioning this cluster.
If not supplied then the credentials of the controller will
be used.
properties:
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
type: string
namespace:
description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
type: string
required:
- name
- namespace
type: object
failureDomains:
description: FailureDomains is an optional field which is
used to assign selected availability zones to a cluster
Expand Down
3 changes: 2 additions & 1 deletion controllers/gcpcluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ type GCPClusterReconciler struct {
// +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=clusters;clusters/status,verbs=get;list;watch
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=gcpclusters,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=gcpclusters/status,verbs=get;update;patch
// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch

func (r *GCPClusterReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
log := log.FromContext(ctx).WithValues("controller", "GCPCluster")
Expand Down Expand Up @@ -133,7 +134,7 @@ func (r *GCPClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
return ctrl.Result{}, nil
}

clusterScope, err := scope.NewClusterScope(scope.ClusterScopeParams{
clusterScope, err := scope.NewClusterScope(ctx, scope.ClusterScopeParams{
Client: r.Client,
Cluster: cluster,
GCPCluster: gcpCluster,
Expand Down
2 changes: 1 addition & 1 deletion controllers/gcpmachine_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ func (r *GCPMachineReconciler) Reconcile(ctx context.Context, req ctrl.Request)
}

// Create the cluster scope
clusterScope, err := scope.NewClusterScope(scope.ClusterScopeParams{
clusterScope, err := scope.NewClusterScope(ctx, scope.ClusterScopeParams{
Client: r.Client,
Cluster: cluster,
GCPCluster: gcpCluster,
Expand Down
Loading