diff --git a/examples/Makefile b/examples/Makefile index 65fcf94f..c3a0eafe 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -2,6 +2,7 @@ all: cd create_pod; make cd list_pod_with_invalid_kubeconfig; make cd list_pod; make + cd list_pod_buffer; make cd list_pod_incluster; make cd delete_pod; make cd exec_provider; make @@ -18,6 +19,7 @@ clean: cd create_pod; make clean cd list_pod_with_invalid_kubeconfig; make clean cd list_pod; make clean + cd list_pod_buffer; make clean cd list_pod_incluster; make clean cd delete_pod; make clean cd exec_provider; make clean @@ -35,6 +37,7 @@ test: kubectl wait --for=condition=ready --all pod -n default --timeout=60s cd list_pod_with_invalid_kubeconfig; make test cd list_pod; make test + cd list_pod_buffer; make test cd delete_pod; make test kubectl wait --for=delete pod/test-pod-6 -n default --timeout=120s cd list_secret; make test @@ -51,6 +54,7 @@ memcheck: kubectl wait --for=condition=ready --all pod -n default --timeout=60s cd list_pod_with_invalid_kubeconfig; make memcheck cd list_pod; make memcheck + cd list_pod_buffer; make memcheck cd delete_pod; make memcheck kubectl wait --for=delete pod/test-pod-6 -n default --timeout=120s cd list_secret; make memcheck diff --git a/examples/list_pod_buffer/.gitignore b/examples/list_pod_buffer/.gitignore new file mode 100644 index 00000000..48417105 --- /dev/null +++ b/examples/list_pod_buffer/.gitignore @@ -0,0 +1 @@ +list_pod_buffer_bin diff --git a/examples/list_pod_buffer/CMakeLists.txt b/examples/list_pod_buffer/CMakeLists.txt new file mode 100644 index 00000000..46a76bab --- /dev/null +++ b/examples/list_pod_buffer/CMakeLists.txt @@ -0,0 +1,4 @@ +find_package(${pkgName} CONFIG REQUIRED COMPONENTS ${pkgName}) + +add_executable(list_pod_buffer main.c) +target_link_libraries(list_pod_buffer PRIVATE ${pkgName}::${pkgName}) \ No newline at end of file diff --git a/examples/list_pod_buffer/Makefile b/examples/list_pod_buffer/Makefile new file mode 100644 index 00000000..efaafc58 --- /dev/null +++ b/examples/list_pod_buffer/Makefile @@ -0,0 +1,17 @@ +INCLUDE:=-I../../kubernetes/ -I/usr/local/include/kubernetes/ +LIBS:=-L../../kubernetes/build -lyaml -lwebsockets -lkubernetes -L/usr/local/lib +CFLAGS:=-g +BIN:=list_pod_buffer_bin + +.PHONY : all clean test memcheck +all: + gcc main.c $(CFLAGS) $(INCLUDE) $(LIBS) -o $(BIN) + +test: + ./$(BIN) + +memcheck: + valgrind --tool=memcheck --leak-check=full ./$(BIN) + +clean: + rm ./$(BIN) diff --git a/examples/list_pod_buffer/main.c b/examples/list_pod_buffer/main.c new file mode 100644 index 00000000..0906f946 --- /dev/null +++ b/examples/list_pod_buffer/main.c @@ -0,0 +1,135 @@ +#include +#include +#include + +#define ENV_KUBECONFIG "KUBECONFIG" +#ifndef _WIN32 +#define ENV_HOME "HOME" +#else +#define ENV_HOME "USERPROFILE" +#endif + +#define KUBE_CONFIG_DEFAULT_LOCATION "%s/.kube/config" + +static char *getWorkingConfigFile(const char *configFileNamePassedIn) +{ + char *configFileName = NULL; + const char *kubeconfig_env = NULL; + const char *homedir_env = NULL; + + if (configFileNamePassedIn) { + configFileName = strdup(configFileNamePassedIn); + } else { + homedir_env = getenv(ENV_HOME); + if (homedir_env) { + int configFileNameSize = strlen(homedir_env) + strlen(KUBE_CONFIG_DEFAULT_LOCATION) + 1; + configFileName = calloc(configFileNameSize, sizeof(char)); + if (configFileName) { + snprintf(configFileName, configFileNameSize, KUBE_CONFIG_DEFAULT_LOCATION, homedir_env); + } + } + } + + return configFileName; +} + +static char *getFileData(const char *filePath) +{ + char *data = NULL; + char *kubeConfigFile = getWorkingConfigFile(filePath); + if (kubeConfigFile) { + FILE *kubeFile = fopen(kubeConfigFile, "r"); + if (kubeFile) { + fseek(kubeFile, 0, SEEK_END); + long fsize = ftell(kubeFile); + fseek(kubeFile, 0, SEEK_SET); + + data = calloc(1, fsize + 1); + if (data) { + fread(data, 1, fsize, kubeFile); + } + + fclose(kubeFile); + } else { + printf("Could not open %s!\n", kubeConfigFile); + } + + free(kubeConfigFile); + } else { + printf("Could not determine the path to kubernetes configuration file! Tried: ENV_KUBECONFIG = %s and ENV_HOME = %s\n", + getenv(ENV_KUBECONFIG), getenv(ENV_HOME) ); + } + + + return data; +} + +void list_pod(apiClient_t * apiClient) +{ + v1_pod_list_t *pod_list = NULL; + pod_list = CoreV1API_listNamespacedPod(apiClient, "default", /*namespace */ + NULL, /* pretty */ + NULL, /* allowWatchBookmarks */ + NULL, /* continue */ + NULL, /* fieldSelector */ + NULL, /* labelSelector */ + NULL, /* limit */ + NULL, /* resourceVersion */ + NULL, /* resourceVersionMatch */ + NULL, /* sendInitialEvents */ + NULL, /* timeoutSeconds */ + NULL /* 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); + } + v1_pod_list_free(pod_list); + pod_list = NULL; + } else { + printf("Cannot get any pod.\n"); + } +} + +int main() +{ + char *basePath = NULL; + sslConfig_t *sslConfig = NULL; + list_t *apiKeys = NULL; + + char *dataBuffer = getFileData(NULL); /* NULL means loading configuration from $HOME/.kube/config */ + if (dataBuffer == NULL) { + printf("Cannot get kubernetes configuration from file.\n"); + return -1; + } + + int rc = load_kube_config_buffer(&basePath, &sslConfig, &apiKeys, dataBuffer); + if (rc != 0) { + printf("Cannot load kubernetes configuration.\n"); + return -1; + } + apiClient_t *apiClient = apiClient_create_with_base_path(basePath, sslConfig, apiKeys); + if (!apiClient) { + printf("Cannot create a kubernetes client.\n"); + return -1; + } + + list_pod(apiClient); + + apiClient_free(apiClient); + apiClient = NULL; + free_client_config(basePath, sslConfig, apiKeys); + basePath = NULL; + sslConfig = NULL; + apiKeys = NULL; + apiClient_unsetupGlobalEnv(); + free(dataBuffer); + dataBuffer = NULL; + + return 0; +} diff --git a/kubernetes/config/kube_config.c b/kubernetes/config/kube_config.c index 115f2088..f6085820 100644 --- a/kubernetes/config/kube_config.c +++ b/kubernetes/config/kube_config.c @@ -304,24 +304,17 @@ static int kuberconfig_auth_provider(kubeconfig_property_t * current_user, kubec return rc; } -int load_kube_config(char **pBasePath, sslConfig_t ** pSslConfig, list_t ** pApiKeys, const char *configFileName) +int load_kube_config_common(char **pBasePath, sslConfig_t ** pSslConfig, list_t ** pApiKeys, kubeconfig_t * kubeconfig) { - static char fname[] = "load_kube_config()"; + static char fname[] = "load_kube_config_common()"; int rc = 0; const kubeconfig_property_t *current_context = NULL; const kubeconfig_property_t *current_cluster = NULL; kubeconfig_property_t *current_user = NULL; - kubeconfig_t *kubeconfig = kubeconfig_create(); - if (!kubeconfig) { - fprintf(stderr, "%s: Cannot create kubeconfig.[%s]\n", fname, strerror(errno)); - return -1; - } - - kubeconfig->fileName = getWorkingConfigFile(configFileName); rc = kubeyaml_load_kubeconfig(kubeconfig); if (0 != rc) { - fprintf(stderr, "%s: Cannot load the kubeconfig %s\n", fname, kubeconfig->fileName); + fprintf(stderr, "%s: Cannot load the kubeconfig %s\n", fname, kubeconfig->fileName ? kubeconfig->fileName : kubeconfig->buffer); rc = -1; goto end; } @@ -393,8 +386,40 @@ int load_kube_config(char **pBasePath, sslConfig_t ** pSslConfig, list_t ** pApi } end: - kubeconfig_free(kubeconfig); - kubeconfig = NULL; + return rc; +} + +int load_kube_config(char **pBasePath, sslConfig_t ** pSslConfig, list_t ** pApiKeys, const char *configFileName) +{ + static char fname[] = "load_kube_config()"; + int rc = 0; + + kubeconfig_t kubeconfig; + memset(&kubeconfig, 0, sizeof(kubeconfig_t)); + + kubeconfig.fileName = getWorkingConfigFile(configFileName); + + rc = load_kube_config_common(pBasePath, pSslConfig, pApiKeys, &kubeconfig); + + kubeconfig_free_members(&kubeconfig); + + return rc; +} + +int load_kube_config_buffer(char **pBasePath, sslConfig_t ** pSslConfig, list_t ** pApiKeys, const char *buffer) +{ + static char fname[] = "load_kube_config_buffer()"; + int rc = 0; + + kubeconfig_t kubeconfig; + memset(&kubeconfig, 0, sizeof(kubeconfig_t)); + + kubeconfig.buffer = strdup(buffer); + + rc = load_kube_config_common(pBasePath, pSslConfig, pApiKeys, &kubeconfig); + + kubeconfig_free_members(&kubeconfig); + return rc; } diff --git a/kubernetes/config/kube_config.h b/kubernetes/config/kube_config.h index 9d29f729..31d06054 100644 --- a/kubernetes/config/kube_config.h +++ b/kubernetes/config/kube_config.h @@ -46,6 +46,44 @@ extern "C" { int load_kube_config(char **pBasePath, sslConfig_t ** pSslConfig, list_t ** pApiKeys, const char *configFileName); +/* + * load_kube_config_buffer + * + * + * Description: + * + * + * Load kubernetes cluster configuration from specfied buffer + * + * + * Return: + * + * 0 Success + * -1 Failed + * + * + * Parameter: + * + * + * IN: + + * buffer : kubernetes cluster configuration data + * + * + * 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_kube_config_buffer(char **pBasePath, sslConfig_t ** pSslConfig, list_t ** pApiKeys, const char *buffer); + /* * free_client_config * diff --git a/kubernetes/config/kube_config_model.c b/kubernetes/config/kube_config_model.c index 8076c596..bcf54094 100644 --- a/kubernetes/config/kube_config_model.c +++ b/kubernetes/config/kube_config_model.c @@ -214,7 +214,7 @@ kubeconfig_t *kubeconfig_create() return config; } -void kubeconfig_free(kubeconfig_t * kubeconfig) +void kubeconfig_free_members(kubeconfig_t * kubeconfig) { if (!kubeconfig) { return; @@ -224,6 +224,10 @@ void kubeconfig_free(kubeconfig_t * kubeconfig) free(kubeconfig->fileName); kubeconfig->fileName = NULL; } + if (kubeconfig->buffer) { + free(kubeconfig->buffer); + kubeconfig->buffer = NULL; + } if (kubeconfig->apiVersion) { free(kubeconfig->apiVersion); kubeconfig->apiVersion = NULL; @@ -252,6 +256,15 @@ void kubeconfig_free(kubeconfig_t * kubeconfig) kubeconfig_properties_free(kubeconfig->contexts, kubeconfig->contexts_count); kubeconfig->contexts = NULL; } +} + +void kubeconfig_free(kubeconfig_t * kubeconfig) +{ + if (!kubeconfig) { + return; + } + + kubeconfig_free_members(kubeconfig); free(kubeconfig); } diff --git a/kubernetes/config/kube_config_model.h b/kubernetes/config/kube_config_model.h index 45833a21..8915f5c7 100644 --- a/kubernetes/config/kube_config_model.h +++ b/kubernetes/config/kube_config_model.h @@ -88,6 +88,7 @@ extern "C" { typedef struct kubeconfig_t { char *fileName; + char *buffer; char *apiVersion; char *preferences; char *kind; @@ -112,9 +113,17 @@ extern "C" { kubeconfig_property_t **kubeconfig_properties_create(int contexts_count, kubeconfig_property_type_t type); void kubeconfig_properties_free(kubeconfig_property_t ** properties, int properties_count); + // allocate kubeconfig_t structure on heap kubeconfig_t *kubeconfig_create(); + + // free a kubeconfig_t structure allocated on heap by a call to kubeconfig_create void kubeconfig_free(kubeconfig_t * kubeconfig); + // free internal members of a kubeconfig_t structure. + // used when releasing resources for a kubeconfig_t that was not allocated using kubeconfig_create + // for example a kubeconfig_t allocated on stack + void kubeconfig_free_members(kubeconfig_t * kubeconfig); + #ifdef __cplusplus } #endif diff --git a/kubernetes/config/kube_config_yaml.c b/kubernetes/config/kube_config_yaml.c index 3007f6eb..33ac0276 100644 --- a/kubernetes/config/kube_config_yaml.c +++ b/kubernetes/config/kube_config_yaml.c @@ -430,7 +430,12 @@ int kubeyaml_load_kubeconfig(kubeconfig_t * kubeconfig) { static char fname[] = "kubeyaml_load_kubeconfig()"; - /* Set a file input. */ + if (kubeconfig->fileName && kubeconfig->buffer) { + fprintf(stderr, "%s: Cannot use both kubeconfig->fileName and kubeconfig->buffer.\n", fname); + return -1; + } + + /* Set a file input or use the provided buffer. */ FILE *input = NULL; if (kubeconfig->fileName) { input = fopen(kubeconfig->fileName, "rb"); @@ -438,17 +443,23 @@ int kubeyaml_load_kubeconfig(kubeconfig_t * kubeconfig) fprintf(stderr, "%s: Cannot open the file %s.[%s]\n", fname, kubeconfig->fileName, strerror(errno)); return -1; } + } else if (kubeconfig->buffer) { + // Nothing to do here for now. } else { - fprintf(stderr, "%s: The kubeconf file name needs be set by kubeconfig->fileName .\n", fname); + fprintf(stderr, "%s: One of the kubeconfig->fileName or kubeconfig->buffer needs to be set.\n", fname); return -1; } + /* Create the Parser object. */ yaml_parser_t parser; yaml_document_t document; - /* Create the Parser object. */ yaml_parser_initialize(&parser); - yaml_parser_set_input_file(&parser, input); + if (input) { + yaml_parser_set_input_file(&parser, input); + } else { + yaml_parser_set_input_string(&parser, (const unsigned char *) kubeconfig->buffer, strlen(kubeconfig->buffer)); + } int done = 0; while (!done) { @@ -469,12 +480,16 @@ int kubeyaml_load_kubeconfig(kubeconfig_t * kubeconfig) /* Cleanup */ yaml_parser_delete(&parser); - fclose(input); + if (input) { + fclose(input); + } return 0; error: yaml_parser_delete(&parser); - fclose(input); + if (input) { + fclose(input); + } return -1; } diff --git a/kubernetes/config/kube_config_yaml.h b/kubernetes/config/kube_config_yaml.h index b162b5cf..82bb73ee 100644 --- a/kubernetes/config/kube_config_yaml.h +++ b/kubernetes/config/kube_config_yaml.h @@ -24,7 +24,10 @@ extern "C" { * * IN: * kubeconfig->fileName: kubernetes cluster configuration file name - * + * kubeconfig->buffer: kubernetes cluster configuration data; this is considered only if kubeconfig->fileName is set to NULL + * + * Note: One may use either kubeconfig->fileName or kubeconfig->buffer but not both at the same time. + * * OUT: * kubeconfig: kubernetes cluster configuration *