aboutsummaryrefslogtreecommitdiff
path: root/src/delivery_docker.c
blob: e1d7f600943338360339becf4999ab2af17832aa (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include "delivery.h"

int delivery_docker(struct Delivery *ctx) {
    if (!docker_capable(&ctx->deploy.docker.capabilities)) {
        return -1;
    }
    char tag[STASIS_NAME_MAX];
    char args[PATH_MAX];
    int has_registry = ctx->deploy.docker.registry != NULL;
    size_t total_tags = strlist_count(ctx->deploy.docker.tags);
    size_t total_build_args = strlist_count(ctx->deploy.docker.build_args);

    if (!has_registry) {
        msg(STASIS_MSG_WARN | STASIS_MSG_L2, "No docker registry defined. You will need to manually retag the resulting image.\n");
    }

    if (!total_tags) {
        char default_tag[PATH_MAX];
        msg(STASIS_MSG_WARN | STASIS_MSG_L2, "No docker tags defined by configuration. Generating default tag(s).\n");
        // generate local tag
        memset(default_tag, 0, sizeof(default_tag));
        sprintf(default_tag, "%s:%s-py%s", ctx->meta.name, ctx->info.build_name, ctx->meta.python_compact);
        tolower_s(default_tag);

        // Add tag
        ctx->deploy.docker.tags = strlist_init();
        strlist_append(&ctx->deploy.docker.tags, default_tag);

        if (has_registry) {
            // generate tag for target registry
            memset(default_tag, 0, sizeof(default_tag));
            sprintf(default_tag, "%s/%s:%s-py%s", ctx->deploy.docker.registry, ctx->meta.name, ctx->info.build_number, ctx->meta.python_compact);
            tolower_s(default_tag);

            // Add tag
            strlist_append(&ctx->deploy.docker.tags, default_tag);
        }
        // regenerate total tag available
        total_tags = strlist_count(ctx->deploy.docker.tags);
    }

    memset(args, 0, sizeof(args));

    // Append image tags to command
    for (size_t i = 0; i < total_tags; i++) {
        char *tag_orig = strlist_item(ctx->deploy.docker.tags, i);
        strcpy(tag, tag_orig);
        docker_sanitize_tag(tag);
        sprintf(args + strlen(args), " -t \"%s\" ", tag);
    }

    // Append build arguments to command (i.e. --build-arg "key=value"
    for (size_t i = 0; i < total_build_args; i++) {
        char *build_arg = strlist_item(ctx->deploy.docker.build_args, i);
        if (!build_arg) {
            break;
        }
        sprintf(args + strlen(args), " --build-arg \"%s\" ", build_arg);
    }

    // Build the image
    char delivery_file[PATH_MAX];
    char dest[PATH_MAX];
    char rsync_cmd[PATH_MAX * 2];
    memset(delivery_file, 0, sizeof(delivery_file));
    memset(dest, 0, sizeof(dest));

    sprintf(delivery_file, "%s/%s.yml", ctx->storage.delivery_dir, ctx->info.release_name);
    if (access(delivery_file, F_OK) < 0) {
        fprintf(stderr, "docker build cannot proceed without delivery file: %s\n", delivery_file);
        return -1;
    }

    sprintf(dest, "%s/%s.yml", ctx->storage.build_docker_dir, ctx->info.release_name);
    if (copy2(delivery_file, dest, CT_PERM)) {
        fprintf(stderr, "Failed to copy delivery file to %s: %s\n", dest, strerror(errno));
        return -1;
    }

    memset(dest, 0, sizeof(dest));
    sprintf(dest, "%s/packages", ctx->storage.build_docker_dir);

    msg(STASIS_MSG_L2, "Copying conda packages\n");
    memset(rsync_cmd, 0, sizeof(rsync_cmd));
    sprintf(rsync_cmd, "rsync -avi --progress '%s' '%s'", ctx->storage.conda_artifact_dir, dest);
    if (system(rsync_cmd)) {
        fprintf(stderr, "Failed to copy conda artifacts to docker build directory\n");
        return -1;
    }

    msg(STASIS_MSG_L2, "Copying wheel packages\n");
    memset(rsync_cmd, 0, sizeof(rsync_cmd));
    sprintf(rsync_cmd, "rsync -avi --progress '%s' '%s'", ctx->storage.wheel_artifact_dir, dest);
    if (system(rsync_cmd)) {
        fprintf(stderr, "Failed to copy wheel artifactory to docker build directory\n");
    }

    if (docker_build(ctx->storage.build_docker_dir, args, ctx->deploy.docker.capabilities.build)) {
        return -1;
    }

    // Test the image
    // All tags point back to the same image so test the first one we see
    // regardless of how many are defined
    strcpy(tag, strlist_item(ctx->deploy.docker.tags, 0));
    docker_sanitize_tag(tag);

    msg(STASIS_MSG_L2, "Executing image test script for %s\n", tag);
    if (ctx->deploy.docker.test_script) {
        if (isempty(ctx->deploy.docker.test_script)) {
            msg(STASIS_MSG_L2 | STASIS_MSG_WARN, "Image test script has no content\n");
        } else {
            int state;
            if ((state = docker_script(tag, ctx->deploy.docker.test_script, 0))) {
                msg(STASIS_MSG_L2 | STASIS_MSG_ERROR, "Non-zero exit (%d) from test script. %s image archive will not be generated.\n", state >> 8, tag);
                // test failed -- don't save the image
                return -1;
            }
        }
    } else {
        msg(STASIS_MSG_L2 | STASIS_MSG_WARN, "No image test script defined\n");
    }

    // Test successful, save image
    if (docker_save(path_basename(tag), ctx->storage.docker_artifact_dir, ctx->deploy.docker.image_compression)) {
        // save failed
        return -1;
    }

    return 0;
}