summaryrefslogtreecommitdiffstats
path: root/adapter/acumos/aoconversion/docker_gen.py
blob: 5f497c942588889a623dd37060c21d2a5a3681b0 (plain)
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
# ============LICENSE_START====================================================
# org.onap.dcae
# =============================================================================
# Copyright (c) 2019 AT&T Intellectual Property. All rights reserved.
# Copyright (c) 2021 highstreet technologies GmbH. All rights reserved.
# =============================================================================
# Licensed 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.
# ============LICENSE_END======================================================

import os
from docker import APIClient
from aoconversion import exceptions, utils


def _generate_dockerfile(meta, model_name, http_proxy, https_proxy, no_proxy):
    """
    bind the templated docker string
    """
    python_version = meta["runtime"]["version"]
    docker_template = f'''
    FROM python:{python_version}

    ENV MODELNAME {model_name}
    RUN mkdir /app
    WORKDIR /app

    ADD ./{model_name} /app/{model_name}
    ADD ./requirements.txt /app
    ENV http_proxy={http_proxy}
    ENV https_proxy={https_proxy}
    ENV HTTP_PROXY={http_proxy}
    ENV HTTPS_PROXY={https_proxy}
    ENV no_proxy={no_proxy}
    ENV NO_PROXY={no_proxy}
    RUN pip install -r /app/requirements.txt && \
        pip install acumos_dcae_model_runner && \
        pip install pyyaml

    ENV DCAEPORT=10000
    EXPOSE $DCAEPORT

    ENTRYPOINT ["acumos_dcae_model_runner"]
    CMD ["/app/{model_name}"]
    '''

    return docker_template


# Public


def build_and_push_docker(config, model_name, model_version="latest"):
    """
    build and push the dcae docker container
    Returns the docker uri so this can be pipelined into specgen
    """
    model_repo_path = config.tmpdir
    dockerfile_path = "{0}/Dockerfile".format(model_repo_path)
    reqs_path = "{0}/requirements.txt".format(model_repo_path)

    # get the metadata
    meta = utils.get_metadata(model_repo_path, model_name)

    # write the reqs file, will be removed later
    reqs = meta["runtime"]["dependencies"]["pip"]["requirements"]
    with open(reqs_path, "w") as f:
        for r in reqs:
            f.write("{0}=={1}\n".format(r["name"], r["version"]))

    # generate the dockerfile
    print("Http_Proxy: {} & Https_Proxy: {}".format(config.http_proxy, config.https_proxy))
    dockerfile = _generate_dockerfile(meta, model_name, config.http_proxy, config.https_proxy, config.no_proxy)

    # write the dockerfile, will be removed later
    with open("{0}/Dockerfile".format(model_repo_path), "w") as f:
        f.write(dockerfile)

    docker_uri = "{0}/{1}:{2}".format(config.dockerregistry, model_name, model_version)

    # do the docker build
    cli = APIClient(base_url=config.dockerhost, user_agent="Docker-Client-xx.yy")
    response = [line.decode() for line in cli.build(path=model_repo_path, rm=True, tag=docker_uri)]

    # clean up the files
    os.remove(dockerfile_path)
    os.remove(reqs_path)

    # parse the Docker response to see whether we succeeded
    for r in response:
        # In some scenarios, for example a non-existing Dockerfile, docker build raises a native exception, see:
        # https://docker-py.readthedocs.io/en/stable/api.html#module-docker.api.build
        # However, if something fails such as a non-existing directory referenced in the dockerfile, NO exception is raised.
        # In this case, one of the console output lines is "errorDetail"....
        if "errorDetail" in r:
            raise exceptions.DockerBuildFail(r)

    # if the above succeeded, we can push
    # push. same problem as above; e.g., "no basic auth credentials" does not throw an exception!
    response = [
        line.decode()
        for line in cli.push(
            repository=docker_uri, auth_config={"username": config.dockeruser, "password": config.dockerpass()}, stream=True
        )
    ]
    for r in response:
        if "errorDetail" in r:
            raise exceptions.DockerPushFail(r)

    return docker_uri