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

Support to add ResoureManagerTags to Compute Instances #992

Closed
bharath-b-rh opened this issue Jul 31, 2023 · 2 comments · Fixed by #1008
Closed

Support to add ResoureManagerTags to Compute Instances #992

bharath-b-rh opened this issue Jul 31, 2023 · 2 comments · Fixed by #1008
Assignees
Labels
kind/feature Categorizes issue or PR as related to a new feature. lifecycle/active Indicates that an issue or PR is actively being worked on by a contributor.

Comments

@bharath-b-rh
Copy link

/kind feature

GCP Tags are key-value pairs that are bind to the GCP resources. Unlike currently supported labels, tags are not part of the resource metadata but resource in itself. Tag Keys, Values, Bindings are all discreet resources. Tags are used for defining IAM policy conditions, Organization conditionals policies and integrating with Cloud billing for cost management, which are not supported by labels.

Describe the solution you'd like

  1. Able to define the list of tags to add to the Compute Instances during cluster creation.
  2. New Compute resources created for the cluster must have the tags defined.

Anything else you would like to add:
Currently tag resources tag keys and tag values can only be created at Organization and Project level with the required permissions. Tag keys and Tag Values will be created by the user and only the Tag bindings to the Compute Instance to be created and which would require below changes

  1. Update GCPClusterSpec with new field for defining tags.
type GCPClusterSpec struct {
	// resourceManagerTags is an optional set of tags to apply to GCP resources managed
        // by the GCP provider. GCP supports a maximum of 50 tags per resource.
	// +kubebuilder:validation:MaxItems=50
	// +listType=map
	// +listMapKey=key
	// +optional
	ResourceManagerTags ResourceManagerTag `json:"resourceManagerTags,omitempty"`
}

// ResourceManagerTag is a tag to apply to GCP resources managed by the GCP provider.
type ResourceManagerTag struct {
	// parentID is the ID of the hierarchical resource where the tags are defined
	// e.g. at the Organization or the Project level. To find the Organization or Project ID ref
	// https://cloud.google.com/resource-manager/docs/creating-managing-organization#retrieving_your_organization_id
	// https://cloud.google.com/resource-manager/docs/creating-managing-projects#identifying_projects
	// An OrganizationID must consist of decimal numbers, and cannot have leading zeroes.
	// A ProjectID must be 6 to 30 characters in length, can only contain lowercase letters,
	// numbers, and hyphens, and must start with a letter, and cannot end with a hyphen.
	// +kubebuilder:validation:Required
	// +kubebuilder:validation:MinLength=1
	// +kubebuilder:validation:MaxLength=32
	// +kubebuilder:validation:Pattern=`(^[1-9][0-9]{0,31}$)|(^[a-z][a-z0-9-]{4,28}[a-z0-9]$)`
	ParentID string `json:"parentID"`

	// key is the key part of the tag. A tag key can have a maximum of 63 characters and cannot
	// be empty. Tag key must begin and end with an alphanumeric character, and must contain
	// only uppercase, lowercase alphanumeric characters, and the following special
        // characters `._-`.
	// +kubebuilder:validation:Required
	// +kubebuilder:validation:MinLength=1
	// +kubebuilder:validation:MaxLength=63
	// +kubebuilder:validation:Pattern=`^[a-zA-Z0-9]([0-9A-Za-z_.-]{0,61}[a-zA-Z0-9])?$`
	Key string `json:"key"`

	// value is the value part of the tag. A tag value can have a maximum of 63 characters and
	// cannot be empty. Tag value must begin and end with an alphanumeric character, and must
	// contain only uppercase, lowercase alphanumeric characters, and the following special
        // characters `_-.@%=+:,*#&(){}[]` and spaces.
	// +kubebuilder:validation:Required
	// +kubebuilder:validation:MinLength=1
	// +kubebuilder:validation:MaxLength=63
	// +kubebuilder:validation:Pattern=`^[a-zA-Z0-9]([0-9A-Za-z_.@%=+:,*#&()\[\]{}\-\s]{0,61}[a-zA-Z0-9])?$`
	Value string `json:"value"`
}
  1. Update GCPMachineSpec with new field for defining tags.
type GCPMachineSpec struct {
	// resourceManagerTags is an optional set of tags to apply to GCP resources managed
        // by the GCP provider. GCP supports a maximum of 50 tags per resource.
	// +kubebuilder:validation:MaxItems=50
	// +listType=map
	// +listMapKey=key
	// +optional
	ResourceManagerTags ResourceManagerTag `json:"resourceManagerTags,omitempty"`
}

ResourceManagerTags can be included during InstanceSpec generation like below

// InstanceSpec returns instance spec.
func (m *MachineScope) InstanceSpec(log logr.Logger) *compute.Instance {
	instance := &compute.Instance{
		Params: &compute.InstanceParams{
			ResourceManagerTags: m.ClusterGetter.ResourceManagerTags().AddResourceManagerTags(m.GCPMachine.Spec.ResourceManagerTags),
		},
	}

Users provide names of the Tag Keys(e.g. foo) and Tag Values(e.g. bar), but the InstanceParams expects the unique IDs generated by GCP service to be provided as below.

// InstanceParams: Additional instance params.
type InstanceParams struct {
	// ResourceManagerTags: Resource manager tags to be bound to the
	// instance. Tag keys and values have the same definition as resource
	// manager tags. Keys must be in the format `tagKeys/{tag_key_id}`, and
	// values are in the format `tagValues/456`. The field is ignored (both
	// PUT & PATCH) when empty.
	ResourceManagerTags map[string]string `json:"resourceManagerTags,omitempty"`
}

And this requires additional logic to transform the user provided input to one as expected by the API.

func getTagValuesNames(ctx context.Context, cred string, tagList []machinev1.ResourceManagerTag) (map[string]string, error) {
	client, err := getTagValuesClient(ctx, cred)
	if err != nil {
		return nil, err
	}
	defer client.Close()

	tagValueList := make(map[string]string, len(tagList))
	getTagValuesReq := &rscmgrpb.GetNamespacedTagValueRequest{}
	for _, tag := range tagList {
		getTagValuesReq.Name = fmt.Sprintf("%s/%s/%s", tag.ParentID, tag.Key, tag.Value)
		value, err := client.GetNamespacedTagValue(ctx, getTagValuesReq)
		if err != nil {
			return nil, err
		}
		tagValueList[value.Parent] = value.Name
	}

	return tagValueList, nil
}

Reference Links

@salasberryfin
Copy link
Contributor

/assign

@richardcase
Copy link
Member

/lifecycle active

@k8s-ci-robot k8s-ci-robot added the lifecycle/active Indicates that an issue or PR is actively being worked on by a contributor. label Aug 29, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/feature Categorizes issue or PR as related to a new feature. lifecycle/active Indicates that an issue or PR is actively being worked on by a contributor.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants