Create/Get a custom kubernetes resource - go

I want to create a custom kubernetes resource with go. The application is deployed in the kubernetes cluster. I want to create e.g. the followng resource:
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: add-response-header
config:
add:
headers:
- "demo: injected-by-kong"
plugin: response-transformer
So far I always created the 'standard' resources e.g. a secret with the following code:
CreateSecret(name string, data map[string]string) error {
confs, err := rest.InClusterConfig()
if err != nil {
panic(err)
}
clientset, err = kubernetes.NewForConfig(confs)
i := clientset.CoreV1()
if _, err := i.Secrets(namespace).Create(&v1.Secret{
TypeMeta: metav1.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
StringData: data,
Type: "Opaque",
}); err != nil {
return err
}
}
In addition I tried to get a resource with the following code:
b, err := clientset.RESTClient().Get().Namespace(namespace).Resource("KongPlugin").DoRaw()
I get the following err:
the server could not find the requested resource (get KongPlugin)
If I make a request at the command line k get KongPlugin I can see all the resources.
NAME PLUGIN-TYPE AGE
add-proxy-headers request-transformer 3h34m
So how can view the custom resoources?

For RESTClient 👇🏼
Get:
You have to fully specify path to you custom resource.
Use fluent interface
data, err := clientset.RESTClient().
Get().
AbsPath("/apis/<api>/<version>").
Namespace("<namespace>").
Resource("kongplugins").
Name("kongplugin-sample").
DoRaw(context.TODO())
or specify manually
data, err := clientset.RESTClient().
Get().
AbsPath("/apis/<api>/<version>/namespaces/<namespace>/kongplugins/kongplugin-sample").
DoRaw(context.TODO())
You can find AbsPath in selfLink of custom resource.
Create:
For example, you can post marshaled data use AbsPath
kongPlugin := &KongPlugin{
TypeMeta: metav1.TypeMeta{
APIVersion: "<api>/<version>",
Kind: "KongPlugin",
},
ObjectMeta: metav1.ObjectMeta{
Name: "kongplugin-sample",
Namespace: "<namespace>",
},
...}}
body, err := json.Marshal(kongPlugin)
data, err := clientset.RESTClient().
Post().
AbsPath("/apis/<api>/<version>/namespaces/<namespace>/kongplugins").
Body(body).
DoRaw(context.TODO())
because arg of the method Body(obj interface{}) is an empty interface, you can use different types of arguments according to documentation:
k8s.io/client-go/rest - func (*Request) Body

Related

Can not create kubernetes secret using go client

I am trying to create a secret using this function
func (ks *KubeSession) DeploySecret(secretName string, secretType string, secretMap map[string][]byte) error {
InfoLogger.Printf("deploying secret %s", secretName)
secret := corev1.Secret{
TypeMeta: metav1.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
},
Data: secretMap,
Type: secretType,
}
_, err := ks.ClientSet.CoreV1().Secrets(JOB_NAMESPACE).Create(context.TODO(), &secret, metav1.CreateOptions{})
if err != nil {
return err
}
return nil
}
It is getting the following error:
cannot use secretType (variable of type string) as "k8s.io/api/core/v1".SecretType value in struct literal
Though, it works fine when I use a hardcoded string instead, like that:
...
Type: "generic"
...
How can I substitute this variable, so that I do not have to use hardcoded values instead?
Resolved.
Found the right way to declare the composite type:
Type: corev1.SecretType(secretType)

k6 - create custom resource using go client

Anyone know how to create a Custom Resource using go-client. basically equivalent of kubectl apply -f 'yaml path'
apiVersion: k6.io/v1alpha1
kind: K6
metadata:
name: k6-sample
spec:
parallelism: 1
#arguments: --out statsd
#cleanup: post
script:
configMap:
name: "staging-stress-test"
file: "staging.js"
Have a go-client code to generate a Custom Resource below
func createk6CR(clientset *kubernetes.Clientset) (string, error) {
k6plugin := &v1alpha1.K6{
TypeMeta: metav1.TypeMeta{
APIVersion: "k6.io/v1alpha1",
Kind: "K6",
},
ObjectMeta: metav1.ObjectMeta{
Name: "k6-sample-1",
Namespace: "default",
},
Spec: v1alpha1.K6Spec{
Parallelism: 3,
Script: v1alpha1.K6Script{
ConfigMap: v1alpha1.K6Configmap{
Name: "staging-stress-test",
File: "staging.js",
},
},
},
// Status: v1alpha1.K6Status{
// Stage: "started",
// },
}
body, err := json.Marshal(k6plugin)
if err != nil {
fmt.Printf("error getting Kubernetes config: %v\n", err)
os.Exit(1)
}
data, err := clientset.RESTClient().
Post().
AbsPath("/apis/k6.io/v1alpha1/namespaces/default/k6").
Body(body).
DoRaw(context.TODO())
if data != nil {
str := string(data[:])
fmt.Printf("return data: %v\n", str)
//os.Exit(1)
}
return "success", err
}
But I get Page 404 not found on AbsPath("/apis/k6.io/v1alpha1/namespaces/default/k6").
Found what was wrong with it, when you do kubectl apply pass it with -v 8 to see the Abspath check for POST
kubectl apply -f 'resource path'
I0816 09:20:56.239402 15535 round_trippers.go:463] POST https://0.0.0.0:43117/apis/k6.io/v1alpha1/namespaces/default/k6s?fieldManager=kubectl-client-side-apply&fieldValidation=Strict
I0816 09:20:56.239428 15535 round_trippers.go:469] Request Headers:
so the code with corrected AbsPath should be below,
data, err := clientset.RESTClient().
Post().
AbsPath("/apis/k6.io/v1alpha1/namespaces/default/k6s").
Body(body).
DoRaw(context.TODO())

List kubernetes resources by clinet-go, How can I get the kind and apiversion?

When I get the kubernetes resources from api with client-go, but I can't found the apiversion and kind in the response, the apiversion and kind is empty. How can I get the apiversion and kind of the resource?
below is my code:
package main
import (
"flag"
"k8s.io/client-go/tools/clientcmd"
"log"
"k8s.io/client-go/kubernetes"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"fmt"
)
var clientset *kubernetes.Clientset
func main() {
k8sconfig := flag.String("k8sconfig","./k8sconfig","kubernetes config file path")
flag.Parse()
config , err := clientcmd.BuildConfigFromFlags("",*k8sconfig)
if err != nil {
log.Println(err)
}
clientset , err = kubernetes.NewForConfig(config)
if err != nil {
log.Fatalln(err)
} else {
fmt.Println("connect k8s success")
}
pods,err := clientset.CoreV1().Pods("").List(metav1.ListOptions{})
if err != nil {
log.Println(err.Error())
}
for _, pod := range pods.Items{
fmt.Println("apiversion: ", pod.APIVersion, "kind: ", pod.Kind)
}
}
The output:
apiversion: kind:
apiversion: kind:
apiversion: kind:
apiversion: kind:
apiversion: kind:
apiversion: kind:
apiversion: kind:
apiversion: kind:
......
......
I think the issue is that you are getting the list of pods using the List() API so it is not a pod as you expect and doesn't have a Kind field.
You need to iterate over the list of pods to access individual pods:
for _, pod := range pods.Items {
fmt.Printf("%s %s\n", pod.GetName(), pod.GetCreationTimestamp())
}
The Kind field is present as part of Pod's Metadata and can be accessed using pod.ObjectMeta.Kind.
You are not getting the APIVersion and Kind because it was ignored from the code.
If you take a look at the API server JSON response, it will be something like below:
{
"kind":"PodList",
"apiVersion":"v1",
"metadata":{
"resourceVersion":"2397"
},
"items":[
{
... ... ...
So the response does contain the APIVersion and Kind. But when the response is decoded to k8s object, here
out, _, err := r.decoder.Decode(r.body, nil, obj)
Here you can see that the second output parameter is ignored, which is schema.GroupVersionKind.
func (c *Something) Decode([]byte, *schema.GroupVersionKind, runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
}
N.B.: When you are making an API call using the client-go (unless dynamic client), you already know the APIVersion (ie. CoreV1() ) and the Kind ( ie. List()).

How do I get/update a Kubernetes custom resource from a go program?

I'm looking for the go equivalent of:
kubectl get some-custom-resource-kind some-custom-resource -o yaml > file.yaml
Modify the yaml file...
kubectl apply -f file.yaml
Kubernetes has a client go library for standard resource kinds.
And various vendors have client go libraries for their custom resources.
But I need to get/update a resource kind that doesn't have a publicly available client go library. The logic is implemented in bash script today and I'm trying to move that function to a go controller.
Seems like it there should be a straightforward way in go to do the equivalent of kubectl.
Thanks, Paul
For any type, including your CRDs, use client.Client.
From the documentation:
// Using a typed object.
pod := &corev1.Pod{}
// c is a created client.
_ = c.Get(context.Background(), client.ObjectKey{
Namespace: "namespace",
Name: "name",
}, pod)
pod.SetFinalizers(append(pod.GetFinalizers(), "new-finalizer"))
_ = c.Update(context.Background(), pod)
// Using a unstructured object.
u := &unstructured.Unstructured{}
u.SetGroupVersionKind(schema.GroupVersionKind{
Group: "apps",
Kind: "Deployment",
Version: "v1",
})
_ = c.Get(context.Background(), client.ObjectKey{
Namespace: "namespace",
Name: "name",
}, u)
u.SetFinalizers(append(u.GetFinalizers(), "new-finalizer"))
_ = c.Update(context.Background(), u)
You could just as easily swap in SomeCustomResourceKind:
myCR := &v1alpha1.SomeCustomResourceKind{}
// c is a created client.Client
_ = c.Get(context.TODO(), client.ObjectKey{
Namespace: "namespace",
Name: "some-custom-resource", }, myCR)
myCR.MyProperty = "NewValue"
_ = c.Update(context.TODO(), myCR)
You mentioned you're trying to move this functionality from a bash script to a Go controller, so it would be worth checking out the Kubebuilder project, which can scaffold out a controller for you (and any additional APIs you might need). It creates fully functional controllers with the controller-runtime Client and wires up all the reconciliation logic to manage your CRDs.
I am creating a CRD (securitycontextconstraint) as follows:
import(
v1 "github.com/openshift/api/security/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func (c *ClusterClient) CreateSCC() {
name := "ssc-test"
scc := v1.SecurityContextConstraints{
TypeMeta: metav1.TypeMeta{
Kind: "SecurityContextConstraints",
APIVersion: "security.openshift.io/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
RequiredDropCapabilities: []corev1.Capability{"KILL", "MKNOD", "SETUID", "SETGID"},
AllowHostPorts: false,
AllowPrivilegedContainer: false,
AllowHostDirVolumePlugin: false,
AllowHostIPC: false,
ReadOnlyRootFilesystem: false,
DefaultAddCapabilities: nil,
AllowHostPID: false,
AllowHostNetwork: false,
AllowedCapabilities: nil,
Volumes: []v1.FSType{v1.FSTypeConfigMap, v1.FSTypeDownwardAPI, v1.FSTypeEmptyDir, v1.FSTypePersistentVolumeClaim, v1.FSProjected, v1.FSTypeSecret},
SELinuxContext: v1.SELinuxContextStrategyOptions{
Type: v1.SELinuxStrategyMustRunAs,
},
RunAsUser: v1.RunAsUserStrategyOptions{
Type: v1.RunAsUserStrategyMustRunAsRange,
},
SupplementalGroups: v1.SupplementalGroupsStrategyOptions{
Type: v1.SupplementalGroupsStrategyRunAsAny,
},
Users: []string{},
Groups: []string{"system:authenticated"},
}
// To check if scc is already created or not
_, err := c.kubeClient.CoreV1().RESTClient().
Get().
AbsPath("/apis/security.openshift.io/v1/securitycontextconstraints/ssc-test").
DoRaw(context.Background())
// scc is not present in cluster
if err != nil {
//Creating SCC
_, err = c.kubeClient.CoreV1().RESTClient().
Post().
AbsPath("/apis/security.openshift.io/v1").
Resource("securitycontextconstraints").
// VersionedParams(&metav1.CreateOptions{}, scheme.ParameterCodec).
Body(&scc).
DoRaw(context.Background())
if err != nil {
fmt.Printf("Failed to create SecurityContextConstraints: %v\n", err)
// return Failure, err
}
fmt.Printf("Successfully created SecurityContextConstraints %s\n", name)
}
}

How to use golang api to do external authentication to kubernetes without kubeconfig file?

While the kubernetes golang api example for out-of-cluster authentication works fine, and creating a service account and exporting the bearer token works great, it feels silly to write the pieces to a temporary file only to tell the API to read it. Is there an API way to pass these pieces as an object rather than write to a file?
clusterData := map[string]string{
"BEARER_TOKEN": bearerToken,
"CA_DATA": clusterCA,
"ENDPOINT": clusterUrl,
}
const kubeConfigTmpl = `
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: {{.CA_DATA}}
server: {{.HOST_IP_ADDRESS}}
name: kubernetes
contexts:
- context:
cluster: kubernetes
namespace: default
user: lamdba-serviceaccount-default-kubernetes
name: lamdba-serviceaccount-default-kubernetes
current-context: lamdba-serviceaccount-default-kubernetes
kind: Config
preferences: {}
users:
- name: lamdba-serviceaccount-default-kubernetes
user:
token: {{.BEARER_TOKEN}}
`
t := template.Must(template.New("registration").Parse(kubeConfigTmpl))
buf := &bytes.Buffer{}
if err := t.Execute(buf, clusterData); err != nil {
panic(err)
}
registrationPayload := buf.String()
d1 := []byte(registrationPayload)
err := ioutil.WriteFile("/tmp/config", d1, 0644)
The rest.Config struct passed to the NewFromConfig client constructors lets you specify bearer tokens and/or client certificate/key data directly.
Looking at the source code, this should work:
// error handling omitted for brevity
cc, _ := clientcmd.NewClientConfigFromBytes([]byte(d1))
config, _ := cc.ClientConfig()
clientset, _ := kubernetes.NewForConfig(config)

Resources