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

🚀 Kubernetes clientset to apply changes from go code in cluster #88

Open
brightsparc opened this issue Feb 15, 2023 · 2 comments
Open
Labels
acknowledged enhancement New feature or request

Comments

@brightsparc
Copy link

Description

I would like to use this operator to create workspace, and module CR from a go client. Could this repo provide a library that could be installed similar to kubernetes that enable creating these resources without having to kube kubectl apply -f directly.

Example client

Following

import (
        "k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	tfclient "github.com/hashicorp/terraform-cloud-operator/client/app.terraform.io/v1alpha2/clientset/versioned"
)

func main() {
	// Load rest config from cluster
	restClient, err := rest.InClusterConfig()
	if err != nil {
		return nil, err
	}
	tfClient, err := seldonclient.NewForConfig(restConfig)
	if err != nil {
		return nil, err
	}
	_, err := tfClient.AppsV1().Workspace(namespace).Get(ctx, "", metav1.GetOptions{})
	if err != nil {
		return nil, err
	}
}

References

If we wanted to execute this from within golang, the alternative would be to use a code generator, or else just call out to kubectl directly.

Community Note

  • Please do not leave "+1" or other comments that do not add relevant new information or questions, they generate extra noise for issue followers and do not help prioritize the request.
  • Please vote on this issue by adding a 👍 reaction to the original issue to help the community and maintainers prioritize this request.
  • If you are interested in working on this issue or have submitted a pull request, please leave a comment.
@jrhouston
Copy link
Contributor

@brightsparc This is an interesting request. Can you share some details of what your use-case is?

@brightsparc
Copy link
Author

@brightsparc This is an interesting request. Can you share some details of what your use-case is?

Right now I am importing the Workspace and Module resources into my project, and using the dynamic client to be able to create/update these by serializing to and from json as shown in the code snippet below.

import (
	// K8s client
	k8sjson "k8s.io/apimachinery/pkg/util/json"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"

	// Include the Terraform Cloud workspace types
	tfcv1alpha2 "github.com/hashicorp/terraform-cloud-operator/api/v1alpha2"
)

func formatWorkspace(workspace *tfcv1alpha2.Workspace) (*unstructured.Unstructured, error) {
	// Marshal the workspace to json, and back again into an unstructured k8s object
	bytes, err := k8sjson.Marshal(workspace)
	if err != nil {
		return nil, err
	}

	var u unstructured.Unstructured
	if err := k8sjson.Unmarshal(bytes, &u); err != nil {
		return nil, err
	}
	return &u, nil
}

func formatModule(module *tfcv1alpha2.Module) (*unstructured.Unstructured, error) {
	// Marshal the module to json, and back again into an unstructured k8s object
	bytes, err := k8sjson.Marshal(module)
	if err != nil {
		return nil, err
	}

	var u unstructured.Unstructured
	if err := k8sjson.Unmarshal(bytes, &u); err != nil {
		return nil, err
	}
	return &u, nil
}

func upsertWorkspaceModule(dynamicClient *dynamic.DynamicClient, ctx context.Context, workspace *tfcv1alpha2.Workspace, module *tfcv1alpha2.Module) (*tfcv1alpha2.Module, error) {
	gvrWorkspace := schema.GroupVersionResource{
		Group:    workspace.GroupVersionKind().Group,
		Version:  workspace.GroupVersionKind().Version,
		Resource: "workspaces",
	}
	gvrModule := schema.GroupVersionResource{
		Group:    module.GroupVersionKind().Group,
		Version:  module.GroupVersionKind().Version,
		Resource: "modules",
	}

	get, err = dynamicClient.Resource(gvrModule).Namespace(module.Namespace).Get(ctx, module.Name, v1.GetOptions{})
	if err != nil {
		if !errors.IsNotFound(err) {
			return nil, fmt.Errorf("error getting module: %s", err.Error())
		}
		// Create new module if it doesn't exist
		u, err := formatModule(module)
		if err != nil {
			return nil, fmt.Errorf("error formating module: %s", err.Error())
		}
		log.Printf("creating module: %s", module.Name)
		output, err = dynamicClient.Resource(gvrModule).Namespace(module.Namespace).Create(ctx, u, v1.CreateOptions{})
		if err != nil {
			return nil, fmt.Errorf("error creating module: %s", err.Error())
		}
	} else {
		// Upate module resource version and restarted at date to re-apply
		module.ResourceVersion = get.GetResourceVersion()
		module.Spec.RestartedAt = time.Now().UTC().Format(time.RFC3339)
		u, err := formatModule(module)
		if err != nil {
			return nil, fmt.Errorf("error formating module: %s", err.Error())
		}
		log.Printf("updating module: %s, version %s", module.Name, u.GetResourceVersion())
		output, err = dynamicClient.Resource(gvrModule).Namespace(module.Namespace).Update(ctx, u, v1.UpdateOptions{})
		if err != nil {
			return nil, fmt.Errorf("error updating module: %s", err.Error())
		}
	}

}

Ideally I would like a managed clientset that has Create and Update methods that I can call directly to remove all this boiler plate code.

@iBrandyJackson iBrandyJackson added enhancement New feature or request acknowledged labels Apr 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
acknowledged enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants