diff --git a/examples/create_pod/main.c b/examples/create_pod/main.c index df74b00..236086a 100644 --- a/examples/create_pod/main.c +++ b/examples/create_pod/main.c @@ -15,14 +15,17 @@ void create_a_pod(apiClient_t * apiClient) podinfo->spec = calloc(1, sizeof(v1_pod_spec_t)); podinfo->metadata = calloc(1, sizeof(v1_object_meta_t)); + /* set pod name */ podinfo->metadata->name = strdup("test-pod-6"); + /* set containers for pod */ list_t *containerlist = list_create(); v1_container_t *con = calloc(1, sizeof(v1_container_t)); con->name = strdup("my-container"); - con->image = strdup("ubuntu:16.04"); + con->image = strdup("ubuntu:latest"); con->image_pull_policy = strdup("IfNotPresent"); + /* set command for container */ list_t *commandlist = list_create(); char *cmd = strdup("sleep"); list_addElement(commandlist, cmd); @@ -33,9 +36,28 @@ void create_a_pod(apiClient_t * apiClient) list_addElement(arglist, arg1); con->args = arglist; + /* set volume mounts for container */ + list_t *volumemounts = list_create(); + v1_volume_mount_t *volmou = v1_volume_mount_create("/test", NULL, "test", 0, NULL, NULL); + list_addElement(volumemounts, volmou); + con->volume_mounts = volumemounts; + list_addElement(containerlist, con); podinfo->spec->containers = containerlist; + /* set volumes for pod */ + list_t *volumelist = list_create(); + v1_volume_t *volume = calloc(1, sizeof(v1_volume_t)); + volume->name = strdup("test"); + + v1_host_path_volume_source_t *hostPath = calloc(1, sizeof(v1_host_path_volume_source_t)); + hostPath->path = strdup("/test"); + volume->host_path = hostPath; + + list_addElement(volumelist, volume); + podinfo->spec->volumes = volumelist; + + /* call API in libkubernetes to create pod */ v1_pod_t *apod = CoreV1API_createNamespacedPod(apiClient, namespace, podinfo, NULL, NULL, NULL); printf("code=%ld\n", apiClient->response_code); diff --git a/examples/list_pod_incluster/.gitignore b/examples/list_pod_incluster/.gitignore new file mode 100644 index 0000000..d867bde --- /dev/null +++ b/examples/list_pod_incluster/.gitignore @@ -0,0 +1 @@ +list_pod_incluster_bin diff --git a/examples/list_pod_incluster/Makefile b/examples/list_pod_incluster/Makefile new file mode 100644 index 0000000..29c6121 --- /dev/null +++ b/examples/list_pod_incluster/Makefile @@ -0,0 +1,8 @@ +INCLUDE:=-I../../kubernetes/include -I../../kubernetes/model -I../../kubernetes/api -I../../kubernetes/config +LIBS:=-L../../kubernetes/build -lkubernetes -lcurl -lyaml -lpthread -lssl -lz +CFLAGS:=-g + +all: + gcc main.c $(CFLAGS) $(INCLUDE) $(LIBS) -o list_pod_incluster_bin +clean: + rm ./list_pod_incluster_bin diff --git a/examples/list_pod_incluster/main.c b/examples/list_pod_incluster/main.c new file mode 100644 index 0000000..db31aaf --- /dev/null +++ b/examples/list_pod_incluster/main.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include +#include +#include + +void list_pod(apiClient_t * apiClient) +{ + v1_pod_list_t *pod_list = NULL; + pod_list = CoreV1API_listNamespacedPod(apiClient, "default", /*namespace */ + NULL, /* pretty */ + 0, /* allowWatchBookmarks */ + NULL, /* continue */ + NULL, /* fieldSelector */ + NULL, /* labelSelector */ + 0, /* limit */ + NULL, /* resourceVersion */ + 0, /* timeoutSeconds */ + 0 /* watch */ + ); + printf("The return code of HTTP request=%ld\n", apiClient->response_code); + if (pod_list) { + printf("Get pod list:\n"); + listEntry_t *listEntry = NULL; + v1_pod_t *pod = NULL; + list_ForEach(listEntry, pod_list->items) { + pod = listEntry->data; + printf("\tThe pod name: %s\n", pod->metadata->name); + } + } else { + printf("Cannot get any pod.\n"); + } +} + +int main(int argc, char *argv[]) +{ + int rc = 0; + + char *baseName = NULL; + sslConfig_t *sslConfig = NULL; + list_t *apiKeys = NULL; + apiClient_t *k8sApiClient = NULL; + + rc = load_incluster_config(&baseName, &sslConfig, &apiKeys); + if (0 == rc) { + k8sApiClient = apiClient_create_with_base_path(baseName, sslConfig, apiKeys); + } else { + printf("Cannot load kubernetes configuration.\n"); + return -1; + } + + if (k8sApiClient) { + list_pod(k8sApiClient); + } + + free_client_config(baseName, sslConfig, apiKeys); + baseName = NULL; + sslConfig = NULL; + apiKeys = NULL; + + apiClient_free(k8sApiClient); + k8sApiClient = NULL; + + return rc; +} diff --git a/kubernetes/CMakeLists.txt b/kubernetes/CMakeLists.txt index 4f29d96..7750c50 100644 --- a/kubernetes/CMakeLists.txt +++ b/kubernetes/CMakeLists.txt @@ -24,6 +24,7 @@ set(SRCS config/kube_config_model.c config/kube_config_yaml.c config/kube_config.c + config/incluster_config.c src/list.c src/apiKey.c src/apiClient.c @@ -778,6 +779,7 @@ set(HDRS config/kube_config_model.h config/kube_config_yaml.h config/kube_config.h + config/incluster_config.h include/apiClient.h include/list.h include/keyValuePair.h diff --git a/kubernetes/config/incluster_config.c b/kubernetes/config/incluster_config.c new file mode 100644 index 0000000..50236b6 --- /dev/null +++ b/kubernetes/config/incluster_config.c @@ -0,0 +1,167 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include "incluster_config.h" + +#define SERVICE_HOST_ENV_NAME "KUBERNETES_SERVICE_HOST" +#define SERVICE_PORT_ENV_NAME "KUBERNETES_SERVICE_PORT" +#define SERVICE_HTTPS_PREFIX "https://" +#define SERVICE_TOKEN_FILENAME "/var/run/secrets/kubernetes.io/serviceaccount/token" +#define SERVICE_CERT_FILENAME "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" +#define AUTH_TOKEN_KEY "Authorization" +#define BEARER_TOKEN_TEMPLATE "Bearer %s" +#define BEARER_TOKEN_BUFFER_SIZE 1024 + +static int checkServiceAccountFile(const char *fileName) +{ + static char fname[] = "checkServiceAccountFile()"; + + if (!fileName) { + fprintf(stderr, "%s: The file name must be specified.\n", fname); + return -1; + } + + struct stat info; + if (-1 == stat(fileName, &info)) { + switch (errno) { + case ENOENT: + fprintf(stderr, "%s: The file %s does not exist.[%s]\n", fname, fileName, strerror(errno)); + return -1; + case EACCES: + fprintf(stderr, "%s: No permission to read the file %s.[%s]\n", fname, fileName, strerror(errno)); + return -1; + default: + fprintf(stderr, "%s: Cannot retrieve the information of file %s.[%s]\n", fname, fileName, strerror(errno)); + return -1; + } + } + + return 0; +} + +static int setBasePathInCluster(char **pBasePath) +{ + static char fname[] = "setBasePathInCluster()"; + + const char *service_host_env = secure_getenv(SERVICE_HOST_ENV_NAME); + if (!service_host_env) { + fprintf(stderr, "%s: Cannot retrieve the kubernetes service host inside a pod by the env %s.\n", fname, SERVICE_HOST_ENV_NAME); + return -1; + } + + const char *service_port_env = secure_getenv(SERVICE_PORT_ENV_NAME); + if (!service_port_env) { + fprintf(stderr, "%s: Cannot retrieve the kubernetes service port inside a pod by the env %s.\n", fname, SERVICE_PORT_ENV_NAME); + return -1; + } + + int basePathSize = strlen(SERVICE_HTTPS_PREFIX) + strlen(service_host_env) + strlen(service_port_env) + 2 /* 1 for ':', 1 for '\0' */ ; + char *basePath = calloc(basePathSize, sizeof(char)); + if (!basePath) { + fprintf(stderr, "%s: Cannot allocate the memory for base path for kubernetes service.\n", fname); + return -1; + } + + snprintf(basePath, basePathSize, "%s%s:%s", SERVICE_HTTPS_PREFIX, service_host_env, service_port_env); + *pBasePath = basePath; + return 0; +} + +static int setSslConfigInCluster(sslConfig_t ** pSslConfig, const char *caFileName) +{ + static char fname[] = "setSslConfigInCluster()"; + + if (0 != checkServiceAccountFile(caFileName)) { + fprintf(stderr, "%s: Cannot retrieve the CA for kubernetes service in the pod.\n", fname); + return -1; + } + + sslConfig_t *sc = sslConfig_create(NULL, /* client certificate */ + NULL, /* client private key */ + caFileName, /* CA file */ + 0 /* 0 means the server certificate is required to be verified by CA */ + ); + if (!sc) { + return -1; + } + + *pSslConfig = sc; + return 0; +} + +static int readTokenInCluster(char *token, int token_buf_size) +{ + static char fname[] = "readTokenInCluster()"; + + if (0 != checkServiceAccountFile(SERVICE_TOKEN_FILENAME)) { + fprintf(stderr, "%s: Cannot retrieve the token for kubernetes service in the pod.\n", fname); + return -1; + } + + FILE *fp; + fp = fopen(SERVICE_TOKEN_FILENAME, "r"); + if (NULL == fp) { + fprintf(stderr, "%s: Failed to open file %s.[%s]", fname, SERVICE_TOKEN_FILENAME, strerror(errno)); + return -1; + } + + char *read_str = fgets(token, token_buf_size, fp); + fclose(fp); + if (NULL == read_str) { + fprintf(stderr, "%s: Cannot read token from the file %s.[%s]", fname, SERVICE_TOKEN_FILENAME, strerror(errno)); + return -1; + } + + return 0; +} + +static int setApiKeysInCluster(list_t ** pApiKeys) +{ + static char fname[] = "setApiKeysInCluster()"; + + list_t *apiKeys = list_create(); + if (!apiKeys) { + fprintf(stderr, "%s: Cannot allocate the memory for api kyes for kubernetes service.\n", fname); + return -1; + } + + char token[BEARER_TOKEN_BUFFER_SIZE]; + memset(token, 0, sizeof(token)); + if (0 == readTokenInCluster(token, BEARER_TOKEN_BUFFER_SIZE)) { + char tokenValue[BEARER_TOKEN_BUFFER_SIZE]; + memset(tokenValue, 0, sizeof(tokenValue)); + snprintf(tokenValue, BEARER_TOKEN_BUFFER_SIZE, BEARER_TOKEN_TEMPLATE, token); + keyValuePair_t *keyPairToken = keyValuePair_create(strdup(AUTH_TOKEN_KEY), strdup(tokenValue)); + list_addElement(apiKeys, keyPairToken); + } else { + fprintf(stderr, "%s: Cannot retrieve the service token in the pod.", fname); + return -1; + } + + *pApiKeys = apiKeys; + return 0; +} + +int load_incluster_config(char **pBasePath, sslConfig_t ** pSslConfig, list_t ** pApiKeys) +{ + static char fname[] = "load_incluster_config()"; + + if (0 != setBasePathInCluster(pBasePath)) { + fprintf(stderr, "%s: Cannot set the base path for API server inside cluster.\n", fname); + return -1; + } + + if (0 != setSslConfigInCluster(pSslConfig, SERVICE_CERT_FILENAME)) { + fprintf(stderr, "%s: Cannot set the SSL Configuration for the client inside cluster.\n", fname); + return -1; + } + + if (0 != setApiKeysInCluster(pApiKeys)) { + fprintf(stderr, "%s: Cannot set the service tokens for the client inside cluster.\n", fname); + return -1; + } + + return 0; +} diff --git a/kubernetes/config/incluster_config.h b/kubernetes/config/incluster_config.h new file mode 100644 index 0000000..34403a8 --- /dev/null +++ b/kubernetes/config/incluster_config.h @@ -0,0 +1,52 @@ +#ifndef _INCLUSTER_CONFIG_H +#define _INCLUSTER_CONFIG_H + +#include "../include/apiClient.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * load_incluster_config + * + * + * Description: + * + * + * Load kubernetes cluster configuration from service account + * files inside a pod + * + * + * Return: + * + * 0 Success + * -1 Failed + * + * + * Parameter: + * + * + * IN: + + * None + * + * + * OUT: + * + * pBasePath: The pointer to API server address + * pSslConfig: The pointer to SSL configuration for client + * pApiKeys: The pointer to API tokens for client + * + * The memory will be allocated inside this function. User + * should call free_client_config to free the memory after + * these parameters are not used. + * + */ + + int load_incluster_config(char **pBasePath, sslConfig_t ** pSslConfig, list_t ** pApiKeys); + +#ifdef __cplusplus +} +#endif +#endif /* _INCLUSTER_CONFIG_H */ diff --git a/kubernetes/config/kube_config.c b/kubernetes/config/kube_config.c index 95a1ec3..2c7c394 100644 --- a/kubernetes/config/kube_config.c +++ b/kubernetes/config/kube_config.c @@ -1,8 +1,10 @@ -#include "kube_config.h" -#include "kube_config_yaml.h" +#define _GNU_SOURCE +#include #include #include #include +#include "kube_config.h" +#include "kube_config_yaml.h" #define ENV_KUBECONFIG "KUBECONFIG" #define ENV_HOME "HOME" @@ -137,15 +139,16 @@ static char *getWorkingConfigFile(const char *configFileNamePassedIn) if (configFileNamePassedIn) { configFileName = strdup(configFileNamePassedIn); } else { - kubeconfig_env = getenv(ENV_KUBECONFIG); + kubeconfig_env = secure_getenv(ENV_KUBECONFIG); if (kubeconfig_env) { configFileName = strdup(kubeconfig_env); } else { - homedir_env = getenv(ENV_HOME); + homedir_env = secure_getenv(ENV_HOME); if (homedir_env) { - configFileName = calloc(strlen(homedir_env) + strlen(KUBE_CONFIG_DEFAULT_LOCATION) + 1, sizeof(char)); + int configFileNameSize = strlen(homedir_env) + strlen(KUBE_CONFIG_DEFAULT_LOCATION) + 1; + configFileName = calloc(configFileNameSize, sizeof(char)); if (configFileName) { - sprintf(configFileName, KUBE_CONFIG_DEFAULT_LOCATION, homedir_env); + snprintf(configFileName, configFileNameSize, KUBE_CONFIG_DEFAULT_LOCATION, homedir_env); } } }