RFC: autostart mongo patch
Pete Zaitcev
zaitcev at redhat.com
Thu Oct 7 20:46:40 UTC 2010
Starting mongod makes a lot of sense for us, so I took the code that
autostarted tabled and adapted it to run mongod. Seems to work acceptably.
The RFC is:
1. This uses -a to cause autostart, which was appropriate for Hail.
For Mongo, we can do this "if we detected no node-wide Mongo,
start our own". Is this what we want? Does it not violate KISS?
2. directory layout is to use current directory... and do it before
the fs back-end changed into local_path (not set for -a).
Is this confusing?
-- Pete
diff --git a/Makefile.am b/Makefile.am
index 19fb253..a704dfa 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -27,6 +27,7 @@ EXTRA_DIST = query.leg
# iwhd is short for Image WareHouse Daemon.
iwhd_SOURCES = \
+ auto.c \
backend.c \
backend.h \
iwh.h \
diff --git a/auto.c b/auto.c
new file mode 100644
index 0000000..5e2c478
--- /dev/null
+++ b/auto.c
@@ -0,0 +1,262 @@
+#include <config.h>
+
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+// #include <httputil.h>
+
+#include <stdarg.h> // for microhttpd.h (bug in there)
+#include <stdint.h> // for microhttpd.h (bug in there)
+#include <microhttpd.h> // for proxy.h
+#include <curl/curl.h> // for proxy.h
+#include <glib.h> // for pfoxy.h
+
+#include "proxy.h"
+#include "iwh.h"
+
+static char *auto_arg_mongod[] = {
+ "mongod",
+ "--port", AUTO_MONGOD_PORT,
+ "--dbpath", AUTO_DIR_DB,
+ /* "--fork", */ /* chdirs god knows where, we cannot use this. */
+ /* "--logpath", AUTO_MONGOD_LOG, */ /* required by --fork */
+ /* "--logappend", */
+ NULL
+};
+
+/* The --quiet option in mongod is useless, so redirect instead. */
+static char *auto_arg_mongod_quiet[] = {
+ "mongod",
+ "--port", AUTO_MONGOD_PORT,
+ "--dbpath", AUTO_DIR_DB,
+ "--logpath", AUTO_MONGOD_LOG,
+ NULL
+};
+
+static int
+auto_mkdir (const char *name)
+{
+ struct stat statb;
+
+ if (mkdir(name, 0777) < 0) {
+ if (errno == EEXIST) {
+ if (stat(name, &statb) < 0) {
+ error (0, errno, "stat %s failed", name);
+ return -1;
+ }
+ if (!S_ISDIR(statb.st_mode)) {
+ error (0, 0, "path %s is not a directory",name);
+ return -1;
+ }
+ return 0;
+ }
+ error(0, errno, "Cannot create %s", name);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+auto_prepare_area (void)
+{
+
+ if (auto_mkdir(AUTO_DIR_FS) < 0) {
+ return -1;
+ }
+ if (auto_mkdir(AUTO_DIR_DB) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+static int
+auto_spawn (const char *prog, char *argv[])
+{
+ struct stat statb;
+ pid_t pid;
+
+ /*
+ * The stat check is purely so that common errors, such as ENOENT
+ * if the program is not available, were printed before the fork.
+ * This serves no security purpose but only makes stderr more tidy.
+ */
+ if (stat(prog, &statb) < 0) {
+ error (0, errno, "stat %s failed", prog);
+ return -1;
+ }
+ if (!S_ISREG(statb.st_mode)) {
+ error (0, 0, "path %s is not a regular file", prog);
+ return -1;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ error (0, errno, "fork failed");
+ return -1;
+ }
+
+ if (pid == 0) {
+ execvp(prog, argv);
+ error (EXIT_FAILURE, errno, "failed to run command %s", prog);
+ }
+
+ /*
+ * This is where you'd normally run waitpid for your daemon, so that
+ * argument check failures were caught at least. In case of mongod,
+ * daemonizing it is a whole can of worms, so we do not. On the
+ * upside, it stays on our session (and process group) and dies
+ * cleanly on keyboard interrupt.
+ */
+
+ return 0;
+}
+
+/*
+ * The server_node and node_resolve are taken verbatim from wait-for-listen.
+ * Is this time to factor?
+ */
+#define ADDRSIZE 40
+
+struct server_node {
+ unsigned alen;
+ union {
+ struct sockaddr addr;
+ unsigned char x[ADDRSIZE];
+ } a;
+};
+
+static int node_resolve(struct server_node *sn,
+ const char *hostname, const char *portstr)
+{
+ struct addrinfo hints;
+ struct addrinfo *res, *res0;
+ int rc;
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+
+ rc = getaddrinfo(hostname, portstr, &hints, &res0);
+ if (rc) {
+ error(0, 0, "getaddrinfo(%s:%s) failed: %s",
+ hostname, portstr, gai_strerror(rc));
+ exit(1);
+ }
+
+ for (res = res0; res; res = res->ai_next) {
+ if (res->ai_family != AF_INET && res->ai_family != AF_INET6)
+ continue;
+
+ if (res->ai_addrlen > ADDRSIZE) /* should not happen */
+ continue;
+
+ memcpy(&sn->a.addr, res->ai_addr, res->ai_addrlen);
+ sn->alen = res->ai_addrlen;
+
+ freeaddrinfo(res0);
+ return 0;
+ }
+
+ freeaddrinfo(res0);
+ return -1;
+}
+
+static int auto_test_mongod(void)
+{
+ struct server_node snode, *sn = &snode;
+ int sfd;
+ int rc;
+
+ memset(sn, 0, sizeof(struct server_node));
+ if (node_resolve(sn, AUTO_HOST, AUTO_MONGOD_PORT) != 0) {
+ /*
+ * Most likely nothing else would work anyway. So abend.
+ */
+ error(0, 0, "unable to resolve host %s port %s",
+ AUTO_HOST, AUTO_MONGOD_PORT);
+ return -1;
+ }
+
+ DPRINTF("trying to connect to mongod (host %s port %s) ...\n",
+ AUTO_HOST, AUTO_MONGOD_PORT);
+
+ sfd = socket(sn->a.addr.sa_family, SOCK_STREAM, 0);
+ if (sfd < 0) {
+ error(0, errno, "socket");
+ return -1;
+ }
+
+ rc = connect(sfd, &sn->a.addr, sn->alen);
+ if (rc != 0) {
+ // if (errno != ECONNREFUSED) {
+ DPRINTF("connect: %s\n", strerror(errno));
+ // }
+ close(sfd);
+ return 1;
+ }
+
+ close(sfd);
+ return 0;
+}
+
+static int
+auto_wait_mongod(void)
+{
+ struct timespec ts;
+ time_t start_time;
+ int rc;
+
+ start_time = time(NULL);
+ for (;;) {
+ rc = auto_test_mongod();
+ if (rc == 0)
+ break;
+ if (time(NULL) >= start_time + 5) {
+ error(0, 0, "failed to verify mongod using port "
+ AUTO_MONGOD_PORT);
+ return -1;
+ }
+
+ ts.tv_sec = 1;
+ ts.tv_nsec = 0;
+ nanosleep(&ts, NULL);
+ }
+ DPRINTF("mongod went up after %ld s\n", (long)time(NULL) - start_time);
+
+ return 0;
+}
+
+int
+auto_start (void)
+{
+ int rc;
+ char **earg;
+
+ if (auto_prepare_area() < 0)
+ return -1;
+
+ rc = auto_test_mongod();
+ if (rc < 0)
+ return -1;
+ if (rc) {
+ DPRINTF("auto-starting mongod\n");
+ earg = verbose ? auto_arg_mongod : auto_arg_mongod_quiet;
+ if (auto_spawn(AUTO_BIN_MONGOD, earg) < 0)
+ return -1;
+ if (auto_wait_mongod() < 0)
+ return -1;
+ }
+
+ DPRINTF("mongod listens on port %s\n", AUTO_MONGOD_PORT);
+ return 0;
+}
diff --git a/iwh.h b/iwh.h
index 091c3c0..d1e5e1d 100644
--- a/iwh.h
+++ b/iwh.h
@@ -66,3 +66,20 @@ GLOBAL(char *, me, "here");
#ifndef ATTRIBUTE_UNUSED
# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
#endif
+
+/*
+ * Common parts of autostart
+ *
+ * Directories are relative so they are based off local_path.
+ * Notice that we continue to use the underscore convention even though
+ * buckets are inside the AUTO_DIR_FS and do not conflict. Visual help:
+ * you can see what to delete right away.
+ *
+ * Mongo is run on the default port so that the node's instance is used if any.
+ */
+#define AUTO_HOST "localhost"
+#define AUTO_DIR_FS "_fs"
+#define AUTO_DIR_DB "_db"
+#define AUTO_BIN_MONGOD "/usr/bin/mongod"
+#define AUTO_MONGOD_LOG "_mongod.log"
+#define AUTO_MONGOD_PORT "27017"
diff --git a/proxy.c b/proxy.c
index 73d67d7..5ead1d2 100644
--- a/proxy.c
+++ b/proxy.c
@@ -266,6 +266,44 @@ err:
return 0;
}
+char *
+auto_config(void)
+{
+ static const char auto_json[] = {
+ "[ {\n"
+ " \"name\": \"fs_autostart\",\n"
+ " \"type\": \"fs\",\n"
+ " \"path\": \"" AUTO_DIR_FS "\"\n"
+ "} ]\n",
+ };
+ json_error_t err;
+
+ if (auto_start() != 0) {
+ return NULL;
+ }
+
+ config = json_loads(auto_json,&err);
+ if (!config) {
+ fprintf(stderr,"JSON error on line %d: %s\n",err.line,err.text);
+ return NULL;
+ }
+
+ if (json_typeof(config) != JSON_ARRAY) {
+ abort();
+ }
+
+ if (json_array_size(config) != 1) {
+ abort();
+ }
+
+ if (!validate_server(0)) {
+ abort();
+ }
+
+ printf("no replication servers defined, auto-start in effect\n");
+ return set_config();
+}
+
static size_t
junk_writer (/* const */ void *ptr, size_t size, size_t nmemb, void *stream)
{
diff --git a/proxy.h b/proxy.h
index 22efcd0..2d79ea1 100644
--- a/proxy.h
+++ b/proxy.h
@@ -31,6 +31,7 @@ typedef struct _provider {
} provider_t;
char *parse_config (void);
+char *auto_config (void);
void repl_init (void);
void replicate (const char *url, size_t size,
const char *policy);
@@ -40,6 +41,9 @@ int get_provider (int i, provider_t *out);
void update_provider (const char *provider,
const char *username, const char *password);
char *get_provider_value (int i, const char *fname);
+char *follow_link (char *object, char *key);
int get_rep_count (void);
+int auto_start (void);
+
#endif
diff --git a/rest.c b/rest.c
index 17dad68..456d2a5 100644
--- a/rest.c
+++ b/rest.c
@@ -69,8 +69,8 @@ typedef struct {
MHD_AccessHandlerCallback handler;
} rule;
-static int fs_mode = 0;
static unsigned short my_port = MY_PORT;
+static int autostart = 0;
const char *program_name;
static char *(reserved_name[]) = { "_default", "_query", "_new", NULL };
@@ -1736,6 +1737,7 @@ enum
};
static const struct option my_options[] = {
+ { "autostart", no_argument, NULL, 'a' },
{ "config", required_argument, NULL, 'c' },
{ "db", required_argument, NULL, 'd' },
{ "master", required_argument, NULL, 'm' },
@@ -1762,6 +1764,7 @@ Usage: %s [OPTION]\n\
Deltacloud image-warehouse daemon.\n\
A configuration file must be specified.\n\
\n\
+ -a, --autostart start necessary back-end services\n\
-c, --config=FILE config file [required]\n\
-d, --db=HOST_PORT database server as ip[:port]\n\
-m, --master=HOST_PORT master (upstream) server as ip[:port]\n\
@@ -1790,7 +1793,10 @@ main (int argc, char **argv)
program_name = argv[0];
- for (;;) switch (getopt_long(argc,argv,"c:d:m:p:v",my_options,NULL)) {
+ for (;;) switch (getopt_long(argc,argv,"ac:d:m:p:v",my_options,NULL)) {
+ case 'a':
+ ++autostart;
+ break;
case 'c':
cfg_file = optarg;
break;
@@ -1832,16 +1838,28 @@ main (int argc, char **argv)
}
args_done:
- if (!cfg_file) {
- error (0, 0, "no configuration file specified");
- usage (EXIT_FAILURE);
- }
-
- me = parse_config();
- if (!me) {
- fprintf(stderr,"could not parse %s\n",cfg_file);
+ if (autostart && cfg_file) {
+ error(0,0,"do not use -c and -a simultaneously");
return !0;
}
+ else if (autostart && !cfg_file) {
+ me = auto_config();
+ if (!me) {
+ /* error printed */
+ return !0;
+ }
+ }
+ else if (!autostart && cfg_file) {
+ me = parse_config();
+ if (!me) {
+ error(0,0,"could not parse %s",cfg_file);
+ return !0;
+ }
+ }
+ else {
+ error(0,0,"specify at least -c or -a");
+ usage (EXIT_FAILURE);
+ }
sem_init(&the_sem,0,0);
if (proxy_host) {
More information about the iwhd-devel
mailing list