Access to metric-server from a pod - kubernetes

I have deployed the metric-server on my kubernetes cluster and it's just working fine as I run the following command:
kubectl get --raw /apis/metrics.k8s.io/v1beta1/nodes
I want to access the metric-server from a pod. For that, I use the service/metric-server's IP and it is in the same namespace that the metric-server is. The way I'm trying to access to the metric is like this:
myurl := fmt.Sprintf("https://%s:%s/apis/metrics.k8s.io/v1beta1/nodes/", serviceHost, servicePort)
u, err := url.Parse(myurl)
if err != nil {
panic(err)
}
req, err := http.NewRequest(httpMethod, u.String(), nil)
if err != nil {
log.Printf("Cant sned req: %s", err)
}
caToken, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token")
if err != nil {
panic(err) // cannot find token file
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", string(caToken)))
caCertPool := x509.NewCertPool()
caCert, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/ca.crt")
if err != nil {
panic(err)
}
caCertPool.AppendCertsFromPEM(caCert)
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: caCertPool,
},
},
}
resp, err := client.Do(req)
if err != nil {
log.Printf("sending helm deploy payload failed: %s", err.Error())
panic(err)
}
This is not working nad the logs result for the pod is:
Get https://METRIC-SERVER-SERVICE-IP/apis/metrics.k8s.io/v1beta1/nodes: x509: certificate is valid for 127.0.0.1, not METRIC-SERVER-SERVICE-IP
Is this the right way to access the metric-server from a pod?

what i did so i can access the metrics-server on a local deployment:
set proper rbac
inside the pod :
export CURL_CA_BUNDLE=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
curl -H "Authorization: Bearer $TOKEN" -k https://10.100.123.57/apis/metrics.k8s.io/v1beta1/nodes

Related

Does anyone know the way to do this on a client-go or the API resources that kubectl describe pod uses

I cannot find the appropriate method to do this.
Does anyone know the way to do this on a client-go or the API resources that kubectl describe pod uses?
Here is an example code of getting pod with client-go:
/*
A demonstration of get pod using client-go
Based on client-go examples: https://github.com/kubernetes/client-go/tree/master/examples
To demonstrate, run this file with `go run <filename> --help` to see usage
*/
package main
import (
"context"
"flag"
"fmt"
"os"
"path/filepath"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
)
func main() {
podName := flag.String("pod-name", "", "name of the required pod")
namespaceName := flag.String("namespace", "", "namespace of the required pod")
var kubeconfig *string
if config, exist := os.LookupEnv("KUBECONFIG"); exist {
kubeconfig = &config
} else if home := homedir.HomeDir(); home != "" {
kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
} else {
kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
}
flag.Parse()
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
if err != nil {
panic(err)
}
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
panic(err)
}
podClient := clientset.CoreV1().Pods(*namespaceName)
fmt.Println("Getting pod...")
result, err := podClient.Get(context.TODO(), *podName, v1.GetOptions{})
if err != nil {
panic(err)
}
// Example fields
fmt.Printf("%+v\n", result.Name)
fmt.Printf("%+v\n", result.Namespace)
fmt.Printf("%+v\n", result.Spec.ServiceAccountName)
}
you can see it on the code of the kubectl describe command

Resource not found error performing SSA create using dynamic client

I was following #ymmt2005 excellent dynamic client guide. All is good until the final step when I make the actual PATCH call, and I get a the server could not find the requested resource error. Just about everything seems right, except I'm unsure about the 'FieldManager' field in the PathOptions struct. I'm not sure what "the actor or entity that is making these changes" refers to. Does this need to match something in my code or system? Any other ideas?
package main
import (
...
)
const resourceYAML = `
apiVersion: apps/v1
kind: Deployment
metadata:
name: mike-nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: 'nginx:latest'
ports:
- containerPort: 80
`
func main() {
ctx := context.Background()
// Create dynamic discovery client from local kubeconfig file
kubePath := filepath.Join(homedir.HomeDir(), ".kube", "config")
cfg, err := clientcmd.BuildConfigFromFlags("", kubePath)
if err != nil {
log.Fatalf("error building config, %v\n", err)
}
dynClient, err := dynamic.NewForConfig(cfg)
if err != nil {
log.Fatalf("error creating client, %v\n", err)
}
disClient, err := discovery.NewDiscoveryClientForConfig(cfg)
if err != nil {
log.Fatalf("error creating discovery client, %v\n", err)
}
// Decode YAML manifest & get GVK
decodeUnstr := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme)
obj := &unstructured.Unstructured{}
_, gvk, err := decodeUnstr.Decode([]byte(resourceYAML), nil, obj)
if err != nil {
log.Fatalf("error decoding manifest, %v\n", err)
}
jsonObj, err := json.Marshal(obj)
if err != nil {
log.Fatalf("error marshaling object, %v\n", err)
}
// Find GVR using GVK
mapper := restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(disClient))
mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
if err != nil {
log.Fatalf("error finding GVR, %v\n", err)
}
// Get REST interface for the GVR, checking for namespace or cluster-wide
var dr dynamic.ResourceInterface
if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
// Namespaced resource
dr = dynClient.Resource(mapping.Resource).Namespace(obj.GetNamespace())
} else {
// Cluster-wide resource
dr = dynClient.Resource(mapping.Resource)
}
// Create or Update the object with SSA
options := metav1.PatchOptions{FieldManager: "sample-controller"}
_, err = dr.Patch(ctx, obj.GetName(), types.ApplyPatchType, jsonObj, options)
if err != nil {
log.Fatalf("error patching, %v\n", err)
}
}
[edit] I confirmed that I was only able to use 'Patch' on a resource that already existed. I tweaked the code to use 'Create' to create the resource, then I was able to successfully do a 'Patch' against it to change. To overcome the FieldManager inconsistencies I added Force=true to the PatchOptions which is recommended in the docs anyway. I'd still like to know how I can create if resource doesn't exist and update if it does--maybe just test for exist?
The answer is really trivial. The original code assumes that namespace is provided in the manifest. The deployment endpoint does not automatically set namespace to default if the provided namespace is "", and errors out because "" is not a valid namespace. Therefore, I added logic to set namespace to default if not provided and presto, the server side apply will create the resource if it doesn't exist and update if it does exist. Thanks again #ymmt2005 .
package main
import (
...
)
const resourceYAML = `
apiVersion: apps/v1
kind: Deployment
metadata:
name: mike-nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: 'nginx:latest'
ports:
- containerPort: 80
`
func main() {
ctx := context.Background()
// Create dynamic discovery client from local kubeconfig file
kubePath := filepath.Join(homedir.HomeDir(), ".kube", "config")
cfg, err := clientcmd.BuildConfigFromFlags("", kubePath)
if err != nil {
log.Fatalf("error building config, %v\n", err)
}
dynClient, err := dynamic.NewForConfig(cfg)
if err != nil {
log.Fatalf("error creating client, %v\n", err)
}
disClient, err := discovery.NewDiscoveryClientForConfig(cfg)
if err != nil {
log.Fatalf("error creating discovery client, %v\n", err)
}
// Decode YAML manifest & get GVK
decodeUnstr := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme)
obj := &unstructured.Unstructured{}
_, gvk, err := decodeUnstr.Decode([]byte(resourceYAML), nil, obj)
if err != nil {
log.Fatalf("error decoding manifest, %v\n", err)
}
jsonObj, err := json.Marshal(obj)
if err != nil {
log.Fatalf("error marshaling object, %v\n", err)
}
// Find GVR using GVK
mapper := restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(disClient))
mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
if err != nil {
log.Fatalf("error finding GVR, %v\n", err)
}
// Set Namespace to default if not provided in manifest
var ns string
if ns = obj.GetNamespace(); ns == "" {
ns = "default"
}
// Get REST interface for the GVR, checking for namespace or cluster-wide
var dr dynamic.ResourceInterface
if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
// Namespaced resource
dr = dynClient.Resource(mapping.Resource).Namespace(ns)
} else {
// Cluster-wide resource
dr = dynClient.Resource(mapping.Resource)
}
// Create or Update the object with SSA
options := metav1.PatchOptions{FieldManager: "sample-controller"}
_, err = dr.Patch(ctx, obj.GetName(), types.ApplyPatchType, jsonObj, options)
if err != nil {
log.Fatalf("error patching, %v\n", err)
}
}

How to restart a deployment in kubernetes using go-client

Is there a way to restart a kubernetes deployment using go-client .I have no idea how to achieve this ,help me!
If you run kubectl rollout restart deployment/my-deploy -v=10 you see that kubectl actually sends a PATCH request to the APIServer and sets the .spec.template.metadata.annotations with something like this:
kubectl.kubernetes.io/restartedAt: '2022-11-29T16:33:08+03:30'
So, you can do this with the client-go:
clientset, err :=kubernetes.NewForConfig(config)
if err != nil {
// Do something with err
}
deploymentsClient := clientset.AppsV1().Deployments(namespace)
data := fmt.Sprintf(`{"spec": {"template": {"metadata": {"annotations": {"kubectl.kubernetes.io/restartedAt": "%s"}}}}}`, time.Now().Format("20060102150405"))
deployment, err := deploymentsClient.Patch(ctx, deployment_name, k8stypes.StrategicMergePatchType, []byte(data), v1.PatchOptions{})
if err != nil {
// Do something with err
}

How do i create a namespace using kubernetes go-client from running container inside a cluster

I have a Kubernetes cluster and have a running container (X). From this container i want to create a new namespace, deploy a pod in this name space and spawn container(Y). I know kubernetes provides REST APIs. however, i am exploring goClient to do the same and not sure how to use namespace creation api.
import (
"github.com/golang/glog"
"k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/pkg/api/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
clientConfig, err := config.createClientConfigFromFile()
if err != nil {
glog.Fatalf("Failed to create a ClientConfig: %v. Exiting.", err)
}
clientset, err := clientset.NewForConfig(clientConfig)
if err != nil {
glog.Fatalf("Failed to create a ClientSet: %v. Exiting.", err)
}
nsSpec := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: ns}}
_, err := clientset.Core().Namespaces().Create(nsSpec)
}
This one is works for me:
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
panic(err)
}
nsName := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "my-new-namespace",
},
}
clientset.CoreV1().Namespaces().Create(context.Background(), nsName, metav1.CreateOptions{})

Error when creating container with golang docker engine

I am creating a project in Go and I am using both "github.com/docker/docker/client" and "github.com/docker/docker/api/types", but when I try and create a container I get the following error:
ERROR: 2016/10/03 22:39:26 containers.go:84: error during connect: Post https://%2Fvar%2Frun%2Fdocker.sock/v1.23/containers/create: http: server gave HTTP response to HTTPS client
I can't understand why this is happening and it only happened after using the new golang docker engine(the old "github.com/docker/engine-api" is now deprecated).
The code isn't anything complicated, so I wonder if I am missing something:
resp, err := cli.Pcli.ContainerCreate(context.Background(), initConfig(), nil, nil, "")
if err != nil {
return err
}
And the initConfig that is called does the following:
func initConfig() (config *container.Config) {
mount := map[string]struct{}{"/root/host": {}}
return &container.Config{Image: "leadis_image", Volumes: mount, Cmd: strslice.StrSlice{"/root/server.py"}, AttachStdout: true}}
Also here is my dockerfile
FROM debian
MAINTAINER Leadis Journey
LABEL Description="This docker image is used to compile and execute user's program."
LABEL Version="0.1"
VOLUME /root/host/
RUN apt-get update && yes | apt-get upgrade
RUN yes | apt-get install gcc g++ python3 make
COPY container.py /root/server.py
EDIT
Just tried to test it with a simpler program
package main
import (
"fmt"
"os"
"io/ioutil"
"github.com/docker/docker/client"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/strslice"
"golang.org/x/net/context"
)
func initConfig() (config *container.Config) {
mount := map[string]struct{}{"/root/host": {}}
return &container.Config{Image: "leadis_image", Volumes: mount, Cmd: strslice.StrSlice{"/root/server.py"}, AttachStdout: true}
}
func main() {
client, _ := client.NewEnvClient()
cwd, _ := os.Getwd()
ctx, err := os.Open(cwd+"/Dockerfile.tar.gz")
if err != nil {
fmt.Println(err)
return
}
build, err := client.ImageBuild(context.Background(), ctx, types.ImageBuildOptions{Tags: []string{"leadis_image"}, Context: ctx, SuppressOutput: false})
if err != nil {
fmt.Println(err)
return
}
b, _ := ioutil.ReadAll(build.Body)
fmt.Println(string(b))
_, err = client.ContainerCreate(context.Background(), initConfig(), nil, nil, "")
if err != nil {
fmt.Println(err)
}
}
Same dockerfile, but I still get the same error:
error during connect: Post
https://%2Fvar%2Frun%2Fdocker.sock/v1.23/containers/create: http:
server gave HTTP response to HTTPS client
client.NewEnvClient()
Last time I tried, this API expects environment variables like DOCKER_HOST in a different syntax from than the normal docker client.
From the client.go:
// NewEnvClient initializes a new API client based on environment variables.
// Use DOCKER_HOST to set the url to the docker server.
// Use DOCKER_API_VERSION to set the version of the API to reach, leave empty for latest.
// Use DOCKER_CERT_PATH to load the TLS certificates from.
// Use DOCKER_TLS_VERIFY to enable or disable TLS verification, off by default.
To use this, you need DOCKER_HOST to be set/exported in one of these formats:
unix:///var/run/docker.sock
http://localhost:2375
https://localhost:2376