Ethanlm commented on a change in pull request #3366: URL: https://github.com/apache/storm/pull/3366#discussion_r669056990
########## File path: storm-core/src/native/worker-launcher/impl/oci/oci_launch_cmd.c ########## @@ -0,0 +1,544 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "utils/cJSON.h" +#include "utils/file-utils.h" +#include "utils/string-utils.h" + +#include "worker-launcher.h" +#include "oci_launch_cmd.h" + +#define SQUASHFS_MEDIA_TYPE "application/vnd.squashfs" + +static void free_olc_layers(olc_layer_spec* layers, unsigned int num_layers) { + unsigned int i; + for (i = 0; i < num_layers; ++i) { + free(layers[i].media_type); + free(layers[i].path); + } + free(layers); +} + +/** + * Free an OCI launch command structure and all memory associated with it. + */ +void free_oci_launch_cmd(oci_launch_cmd* olc) { + if (olc != NULL) { + free(olc->username); + free(olc->container_id); + free(olc->pid_file); + free(olc->script_path); + free_olc_layers(olc->layers, olc->num_layers); + cJSON_Delete(olc->config.hostname); + cJSON_Delete(olc->config.linux_config); + cJSON_Delete(olc->config.mounts); + cJSON_Delete(olc->config.process.args); + cJSON_Delete(olc->config.process.cwd); + cJSON_Delete(olc->config.process.env); + free(olc); + } +} + +static cJSON* parse_json_file(const char* filename) { + char* data = read_file_to_string_as_wl_user(filename); + if (data == NULL) { + fprintf(ERRORFILE, "ERROR: Cannot read command file %s\n", filename); + return NULL; + } + + const char* parse_error_location = NULL; + cJSON* json = cJSON_ParseWithOpts(data, &parse_error_location, 1); + if (json == NULL) { + fprintf(ERRORFILE, "ERROR: Error parsing command file %s at byte offset %ld\n", + filename, parse_error_location - data); + } + + free(data); + return json; +} + +static bool parse_oci_launch_cmd_layer(olc_layer_spec* layer_out, + const cJSON* layer_json) { + if (!cJSON_IsObject(layer_json)) { + fputs("ERROR: OCI launch command layer is not an object\n", ERRORFILE); + return false; + } + + const cJSON* media_type_json = cJSON_GetObjectItemCaseSensitive(layer_json, + "mediaType"); + if (!cJSON_IsString(media_type_json)) { + fputs("ERROR: Bad/Missing media type for OCI launch command layer\n", ERRORFILE); + return false; + } + + const cJSON* path_json = cJSON_GetObjectItemCaseSensitive(layer_json, "path"); + if (!cJSON_IsString(path_json)) { + fputs("ERROR: Bad/Missing path for OCI launch command layer\n", ERRORFILE); + return false; + } + + layer_out->media_type = strdup(media_type_json->valuestring); + layer_out->path = strdup(path_json->valuestring); + return true; +} + +static olc_layer_spec* parse_oci_launch_cmd_layers(unsigned int* num_layers_out, + const cJSON* layers_json) { + if (!cJSON_IsArray(layers_json)) { + fputs("ERROR: Bad/Missing OCI launch command layers\n", ERRORFILE); + return NULL; + } + + unsigned int num_layers = (unsigned int) cJSON_GetArraySize(layers_json); + if (num_layers <= 0) { + return NULL; + } + + olc_layer_spec* layers = calloc(num_layers, sizeof(*layers)); + if (layers == NULL) { + fprintf(ERRORFILE, "ERROR: Cannot allocate memory for %d layers\n", + num_layers + 1); + return NULL; + } + + unsigned int layer_index = 0; + const cJSON* e; + cJSON_ArrayForEach(e, layers_json) { + if (layer_index >= num_layers) { + fputs("ERROR: Iterating past end of layer array\n", ERRORFILE); + free_olc_layers(layers, layer_index); + return NULL; + } + + if (!parse_oci_launch_cmd_layer(&layers[layer_index], e)) { + free_olc_layers(layers, layer_index); + return NULL; + } + + ++layer_index; + } + + *num_layers_out = layer_index; + return layers; +} + +static int parse_reap_layers_keep(cJSON* json) { + if (!cJSON_IsNumber(json)) { + fputs("ERROR: Bad/Missing OCI reap layer keep number\n", ERRORFILE); + return -1; + } + return json->valueint; +} + +static void parse_oci_launch_cmd_oci_config(oci_config* oc, cJSON* oc_json) { + if (!cJSON_IsObject(oc_json)) { + fputs("ERROR: Bad/Missing OCI runtime config in launch command\n", ERRORFILE); + return; + } + oc->hostname = cJSON_DetachItemFromObjectCaseSensitive(oc_json, "hostname"); + oc->linux_config = cJSON_DetachItemFromObjectCaseSensitive(oc_json, "linux"); + oc->mounts = cJSON_DetachItemFromObjectCaseSensitive(oc_json, "mounts"); + + cJSON* process_json = cJSON_GetObjectItemCaseSensitive(oc_json, "process"); + if (!cJSON_IsObject(process_json)) { + fputs("ERROR: Bad/Missing process section in OCI config\n", ERRORFILE); + return; + } + oc->process.args = cJSON_DetachItemFromObjectCaseSensitive( + process_json, "args"); + oc->process.cwd = cJSON_DetachItemFromObjectCaseSensitive( + process_json, "cwd"); + oc->process.env = cJSON_DetachItemFromObjectCaseSensitive( + process_json, "env"); +} + +static bool is_valid_layer_media_type(char* media_type) { + if (media_type == NULL) { + return false; + } + + if (strcmp(SQUASHFS_MEDIA_TYPE, media_type)) { + fprintf(ERRORFILE, "ERROR: Unrecognized layer media type: %s\n", media_type); + return false; + } + + return true; +} + +static bool is_valid_oci_launch_cmd_layers(olc_layer_spec* layers, + unsigned int num_layers) { + if (layers == NULL) { + return false; + } + unsigned int i; + for (i = 0; i < num_layers; ++i) { + if (!is_valid_layer_media_type(layers[i].media_type)) { + return false; + } + if (layers[i].path == NULL) { + return false; + } + } + + return true; +} + +static bool is_valid_oci_config_linux_resources(const cJSON* oclr) { + if (!cJSON_IsObject(oclr)) { + fputs("ERROR: OCI config linux resources missing or not an object\n", ERRORFILE); + return false; + } + + bool all_sections_ok = true; + const cJSON* e; + cJSON_ArrayForEach(e, oclr) { + if (strcmp("blockIO", e->string) == 0) { + // block I/O settings allowed + } else if (strcmp("cpu", e->string) == 0) { + // cpu settings allowed + } else if (strcmp("memory", e->string) == 0) { + // memory settings allowed. (added for storm. hadoop doesn't allow this) + } else { + fprintf(ERRORFILE, + "ERROR: Unrecognized OCI config linux resources element: %s\n", e->string); + all_sections_ok = false; + } + } + + return all_sections_ok; +} + +static bool is_valid_oci_config_linux_seccomp(const cJSON* ocls) { Review comment: secomp file is a little more involved so leaving it for future. ########## File path: storm-core/src/native/worker-launcher/impl/oci/oci_launch_cmd.c ########## @@ -0,0 +1,544 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "utils/cJSON.h" +#include "utils/file-utils.h" +#include "utils/string-utils.h" + +#include "worker-launcher.h" +#include "oci_launch_cmd.h" + +#define SQUASHFS_MEDIA_TYPE "application/vnd.squashfs" + +static void free_olc_layers(olc_layer_spec* layers, unsigned int num_layers) { + unsigned int i; + for (i = 0; i < num_layers; ++i) { + free(layers[i].media_type); + free(layers[i].path); + } + free(layers); +} + +/** + * Free an OCI launch command structure and all memory associated with it. + */ +void free_oci_launch_cmd(oci_launch_cmd* olc) { + if (olc != NULL) { + free(olc->username); + free(olc->container_id); + free(olc->pid_file); + free(olc->script_path); + free_olc_layers(olc->layers, olc->num_layers); + cJSON_Delete(olc->config.hostname); + cJSON_Delete(olc->config.linux_config); + cJSON_Delete(olc->config.mounts); + cJSON_Delete(olc->config.process.args); + cJSON_Delete(olc->config.process.cwd); + cJSON_Delete(olc->config.process.env); + free(olc); + } +} + +static cJSON* parse_json_file(const char* filename) { + char* data = read_file_to_string_as_wl_user(filename); + if (data == NULL) { + fprintf(ERRORFILE, "ERROR: Cannot read command file %s\n", filename); + return NULL; + } + + const char* parse_error_location = NULL; + cJSON* json = cJSON_ParseWithOpts(data, &parse_error_location, 1); + if (json == NULL) { + fprintf(ERRORFILE, "ERROR: Error parsing command file %s at byte offset %ld\n", + filename, parse_error_location - data); + } + + free(data); + return json; +} + +static bool parse_oci_launch_cmd_layer(olc_layer_spec* layer_out, + const cJSON* layer_json) { + if (!cJSON_IsObject(layer_json)) { + fputs("ERROR: OCI launch command layer is not an object\n", ERRORFILE); + return false; + } + + const cJSON* media_type_json = cJSON_GetObjectItemCaseSensitive(layer_json, + "mediaType"); + if (!cJSON_IsString(media_type_json)) { + fputs("ERROR: Bad/Missing media type for OCI launch command layer\n", ERRORFILE); + return false; + } + + const cJSON* path_json = cJSON_GetObjectItemCaseSensitive(layer_json, "path"); + if (!cJSON_IsString(path_json)) { + fputs("ERROR: Bad/Missing path for OCI launch command layer\n", ERRORFILE); + return false; + } + + layer_out->media_type = strdup(media_type_json->valuestring); + layer_out->path = strdup(path_json->valuestring); + return true; +} + +static olc_layer_spec* parse_oci_launch_cmd_layers(unsigned int* num_layers_out, + const cJSON* layers_json) { + if (!cJSON_IsArray(layers_json)) { + fputs("ERROR: Bad/Missing OCI launch command layers\n", ERRORFILE); + return NULL; + } + + unsigned int num_layers = (unsigned int) cJSON_GetArraySize(layers_json); + if (num_layers <= 0) { + return NULL; + } + + olc_layer_spec* layers = calloc(num_layers, sizeof(*layers)); + if (layers == NULL) { + fprintf(ERRORFILE, "ERROR: Cannot allocate memory for %d layers\n", + num_layers + 1); + return NULL; + } + + unsigned int layer_index = 0; + const cJSON* e; + cJSON_ArrayForEach(e, layers_json) { + if (layer_index >= num_layers) { + fputs("ERROR: Iterating past end of layer array\n", ERRORFILE); + free_olc_layers(layers, layer_index); + return NULL; + } + + if (!parse_oci_launch_cmd_layer(&layers[layer_index], e)) { + free_olc_layers(layers, layer_index); + return NULL; + } + + ++layer_index; + } + + *num_layers_out = layer_index; + return layers; +} + +static int parse_reap_layers_keep(cJSON* json) { + if (!cJSON_IsNumber(json)) { + fputs("ERROR: Bad/Missing OCI reap layer keep number\n", ERRORFILE); + return -1; + } + return json->valueint; +} + +static void parse_oci_launch_cmd_oci_config(oci_config* oc, cJSON* oc_json) { + if (!cJSON_IsObject(oc_json)) { + fputs("ERROR: Bad/Missing OCI runtime config in launch command\n", ERRORFILE); + return; + } + oc->hostname = cJSON_DetachItemFromObjectCaseSensitive(oc_json, "hostname"); + oc->linux_config = cJSON_DetachItemFromObjectCaseSensitive(oc_json, "linux"); + oc->mounts = cJSON_DetachItemFromObjectCaseSensitive(oc_json, "mounts"); + + cJSON* process_json = cJSON_GetObjectItemCaseSensitive(oc_json, "process"); + if (!cJSON_IsObject(process_json)) { + fputs("ERROR: Bad/Missing process section in OCI config\n", ERRORFILE); + return; + } + oc->process.args = cJSON_DetachItemFromObjectCaseSensitive( + process_json, "args"); + oc->process.cwd = cJSON_DetachItemFromObjectCaseSensitive( + process_json, "cwd"); + oc->process.env = cJSON_DetachItemFromObjectCaseSensitive( + process_json, "env"); +} + +static bool is_valid_layer_media_type(char* media_type) { + if (media_type == NULL) { + return false; + } + + if (strcmp(SQUASHFS_MEDIA_TYPE, media_type)) { + fprintf(ERRORFILE, "ERROR: Unrecognized layer media type: %s\n", media_type); + return false; + } + + return true; +} + +static bool is_valid_oci_launch_cmd_layers(olc_layer_spec* layers, + unsigned int num_layers) { + if (layers == NULL) { + return false; + } + unsigned int i; + for (i = 0; i < num_layers; ++i) { + if (!is_valid_layer_media_type(layers[i].media_type)) { + return false; + } + if (layers[i].path == NULL) { + return false; + } + } + + return true; +} + +static bool is_valid_oci_config_linux_resources(const cJSON* oclr) { + if (!cJSON_IsObject(oclr)) { + fputs("ERROR: OCI config linux resources missing or not an object\n", ERRORFILE); + return false; + } + + bool all_sections_ok = true; + const cJSON* e; + cJSON_ArrayForEach(e, oclr) { + if (strcmp("blockIO", e->string) == 0) { + // block I/O settings allowed + } else if (strcmp("cpu", e->string) == 0) { + // cpu settings allowed + } else if (strcmp("memory", e->string) == 0) { + // memory settings allowed. (added for storm. hadoop doesn't allow this) + } else { + fprintf(ERRORFILE, + "ERROR: Unrecognized OCI config linux resources element: %s\n", e->string); + all_sections_ok = false; + } + } + + return all_sections_ok; +} + +static bool is_valid_oci_config_linux_seccomp(const cJSON* ocls) { Review comment: validating secomp file is a little more involved so leaving it for future. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: dev-unsubscr...@storm.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org