/*! * Handle commands to toggle the state of the Hypervisor * (between paused and running). * * \param sub \ref subcommand. * \param config \ref cc_oci_config. * \param argc Argument count. * \param argv Argument vector. * \param pause If \c true, pause the VM, else resume it. * * \return \c true on success, else \c false. */ gboolean handle_command_toggle (const struct subcommand *sub, struct cc_oci_config *config, int argc, char *argv[], gboolean pause) { struct oci_state *state = NULL; gchar *config_file = NULL; gboolean ret; const gchar *action; g_assert (sub); g_assert (config); action = pause ? "pause" : "resume"; if (handle_default_usage (argc, argv, sub->name, &ret, 1, NULL)) { return ret; } /* Used to allow us to find the state file */ config->optarg_container_id = argv[0]; ret = cc_oci_get_config_and_state (&config_file, config, &state); if (! ret) { goto out; } /* Transfer certain state elements to config to allow the state * * file to be rewritten with full details. */ if (! cc_oci_config_update (config, state)) { goto out; } ret = cc_oci_toggle (config, state, pause); if (! ret) { goto out; } ret = true; g_print ("%sd container %s\n", action, config->optarg_container_id); out: g_free_if_set (config_file); cc_oci_state_free (state); if (! ret) { g_critical ("failed to %s container %s\n", action, config->optarg_container_id); } return ret; }
/*! * Determine the containers config file, its configuration * and state. * * \param[out] config_file Dynamically-allocated path to containers * config file. * \param[out] config \ref cc_oci_config. * \param[out] state \ref oci_state. * * \note Used by the "stop" command. * * \return \c true on success, else \c false. */ gboolean cc_oci_get_config_and_state (gchar **config_file, struct cc_oci_config *config, struct oci_state **state) { if ((!config_file) || (!config) || (!state)) { return false; } if (! cc_oci_runtime_path_get (config)) { return false; } if (! cc_oci_state_file_get (config)) { return false; } *state = cc_oci_state_file_read (config->state.state_file_path); if (! (*state)) { g_critical("failed to read state file for container %s", config->optarg_container_id); goto err; } /* Fill in further details to make the config valid */ config->bundle_path = g_strdup ((*state)->bundle_path); config->state.workload_pid = (*state)->pid; config->state.status = (*state)->status; g_strlcpy (config->state.comms_path, (*state)->comms_path, sizeof (config->state.comms_path)); g_strlcpy (config->state.procsock_path, (*state)->procsock_path, sizeof (config->state.procsock_path)); *config_file = cc_oci_config_file_path ((*state)->bundle_path); if (! (*config_file)) { goto err; } return true; err: cc_oci_state_free (*state); g_free_if_set (*config_file); return false; }
} END_TEST START_TEST(test_cc_oci_state_free) { struct oci_state *state = g_new0 (struct oci_state, 1); ck_assert(state); state->bundle_path = g_strdup("bundle_path"); state->comms_path = g_strdup("comms_path"); state->procsock_path = g_strdup("procsock_path"); state->id = g_strdup("id"); state->oci_version = g_strdup("oci_version"); state->mounts = g_slist_append(state->mounts, g_new0(struct cc_oci_mount, 1)); state->annotations = g_slist_append(state->annotations, g_new0(struct oci_cfg_annotation, 1)); /* memory leaks will be detected by valgrind */ cc_oci_state_free(state); } END_TEST
/*! * Handle commands to stop the Hypervisor cleanly. * * \param sub \ref subcommand. * \param config \ref cc_oci_config. * \param argc Argument count. * \param argv Argument vector. * * \return \c true on success, else \c false. */ gboolean handle_command_stop (const struct subcommand *sub, struct cc_oci_config *config, int argc, char *argv[]) { struct oci_state *state = NULL; gchar *config_file = NULL; gboolean ret; GNode* root = NULL; g_assert (sub); g_assert (config); if (handle_default_usage (argc, argv, sub->name, &ret, 1, NULL)) { return ret; } /* Used to allow us to find the state file */ config->optarg_container_id = argv[0]; /* FIXME: deal with containerd calling "delete" twice */ if (! cc_oci_state_file_exists (config)) { g_warning ("state file does not exist for container %s", config->optarg_container_id); /* don't make this fatal to keep containerd happy */ ret = true; goto out; } ret = cc_oci_get_config_and_state (&config_file, config, &state); if (! ret) { goto out; } /* convert json file to GNode */ if (! cc_oci_json_parse(&root, config_file)) { goto out; } #ifdef DEBUG /* show json file converted to GNode */ cc_oci_node_dump(root); #endif /*DEBUG*/ if (! cc_oci_process_config(root, config, stop_spec_handlers)) { g_critical ("failed to process config"); goto out; } g_free_node(root); ret = cc_oci_stop (config, state); if (! ret) { goto out; } ret = true; g_print ("stopped container %s\n", config->optarg_container_id); out: g_free_if_set (config_file); cc_oci_state_free (state); if (! ret) { g_critical ("failed to stop container %s\n", config->optarg_container_id); } return ret; }
} END_TEST START_TEST(test_cc_oci_state_file_read) { struct oci_state *state = NULL; /* Needs: * * - ociVersion * - id * - pid * - bundlePath * - commsPath * - processPath * - status */ ck_assert(! cc_oci_state_file_read(NULL)); ck_assert(! cc_oci_state_file_read("/abc/123/xyz")); ck_assert(! cc_oci_state_file_read(TEST_DATA_DIR "/state-no-bundlePath.json")); ck_assert(! cc_oci_state_file_read(TEST_DATA_DIR "/state-no-ociVersion.json")); ck_assert(! cc_oci_state_file_read(TEST_DATA_DIR "/state-no-id.json")); ck_assert(! cc_oci_state_file_read(TEST_DATA_DIR "/state-no-commsPath.json")); ck_assert(! cc_oci_state_file_read(TEST_DATA_DIR "/state-no-processPath.json")); ck_assert(! cc_oci_state_file_read(TEST_DATA_DIR "/state-no-console.json")); ck_assert(! cc_oci_state_file_read(TEST_DATA_DIR "/state-no-console-path.json")); ck_assert(! cc_oci_state_file_read(TEST_DATA_DIR "/state-no-console-socket.json")); ck_assert(! cc_oci_state_file_read(TEST_DATA_DIR "/state-no-vm-object.json")); /* Annotations are optional*/ state = cc_oci_state_file_read(TEST_DATA_DIR "/state-no-annotations.json"); ck_assert (state); cc_oci_state_free (state); /* Mounts are optional */ state = cc_oci_state_file_read(TEST_DATA_DIR "/state-no-mounts.json"); ck_assert (state); cc_oci_state_free (state); state = cc_oci_state_file_read(TEST_DATA_DIR "/state-mounts-no-mount-destination.json"); ck_assert (state); cc_oci_state_free (state); state = cc_oci_state_file_read(TEST_DATA_DIR "/state-mounts-no-mount-directory_created.json"); ck_assert (state); cc_oci_state_free (state); state = cc_oci_state_file_read(TEST_DATA_DIR "/state.json"); ck_assert(state); ck_assert(state->oci_version); ck_assert(state->id); ck_assert(state->pid); ck_assert(state->bundle_path); ck_assert(state->comms_path); ck_assert(state->procsock_path); ck_assert(state->status); ck_assert(state->annotations); cc_oci_state_free(state); } END_TEST