Scott Baker | 7581c25 | 2016-05-27 13:12:47 -0700 | [diff] [blame] | 1 | import os |
| 2 | import base64 |
| 3 | import string |
| 4 | import sys |
Scott Baker | b0eb23e | 2016-06-01 16:08:04 -0700 | [diff] [blame] | 5 | import urllib2 |
| 6 | import urlparse |
Scott Baker | 7581c25 | 2016-05-27 13:12:47 -0700 | [diff] [blame] | 7 | import xmlrpclib |
| 8 | |
Scott Baker | 7581c25 | 2016-05-27 13:12:47 -0700 | [diff] [blame] | 9 | from xos.config import Config |
| 10 | from core.models import Service, ServiceController, ServiceControllerResource |
| 11 | from xos.logger import Logger, logging |
| 12 | |
| 13 | logger = Logger(level=logging.INFO) |
| 14 | |
| 15 | class XOSBuilder(object): |
Scott Baker | 037f679 | 2016-06-02 16:47:45 -0700 | [diff] [blame^] | 16 | UI_KINDS=["models", "admin", "django_library", "rest", "tosca_custom_types", "tosca_resource","public_key"] |
Scott Baker | 18c8917 | 2016-06-02 16:40:25 -0700 | [diff] [blame] | 17 | SYNC_CONTROLLER_KINDS=["synchronizer", "private_key", "public_key"] |
Scott Baker | 513ea45 | 2016-06-02 16:03:26 -0700 | [diff] [blame] | 18 | SYNC_ALLCONTROLLER_KINDS=["models", "django_library"] |
Scott Baker | b0eb23e | 2016-06-01 16:08:04 -0700 | [diff] [blame] | 19 | |
Scott Baker | 7581c25 | 2016-05-27 13:12:47 -0700 | [diff] [blame] | 20 | def __init__(self): |
Scott Baker | b0eb23e | 2016-06-01 16:08:04 -0700 | [diff] [blame] | 21 | self.source_ui_image = "xosproject/xos" |
| 22 | self.source_sync_image = "xosproject/xos-synchronizer-openstack" |
| 23 | self.build_dir = "/opt/xos/BUILD/" |
| 24 | |
| 25 | # stuff that has to do with downloading |
| 26 | |
| 27 | def get_dest_dir(self, scr): |
| 28 | xos_base = "opt/xos" |
| 29 | service_name = scr.service_controller.name |
| 30 | base_dirs = {"models": "%s/services/%s/" % (xos_base, service_name), |
| 31 | "admin": "%s/services/%s/" % (xos_base, service_name), |
| 32 | "django_library": "%s/services/%s/" % (xos_base, service_name), |
| 33 | "synchronizer": "%s/synchronizers/%s/" % (xos_base, service_name), |
| 34 | "tosca_custom_types": "%s/tosca/custom_types/" % (xos_base), |
Scott Baker | 18c8917 | 2016-06-02 16:40:25 -0700 | [diff] [blame] | 35 | "tosca_resource": "%s/tosca/resources/" % (xos_base), |
| 36 | "private_key": "%s/services/%s/keys" % (xos_base, service_name), |
| 37 | "public_key": "%s/services/%s/keys/" % (xos_base, service_name)} |
Scott Baker | b0eb23e | 2016-06-01 16:08:04 -0700 | [diff] [blame] | 38 | return base_dirs[scr.kind] |
| 39 | |
| 40 | def get_build_fn(self, scr): |
| 41 | dest_dir = self.get_dest_dir(scr) |
| 42 | dest_fn = os.path.split(urlparse.urlsplit(scr.full_url).path)[-1] |
| 43 | return os.path.join(dest_dir, dest_fn) |
| 44 | |
| 45 | def get_download_fn(self, scr): |
| 46 | dest_fn = self.get_build_fn(scr) |
| 47 | return os.path.join(self.build_dir, dest_fn) |
| 48 | |
| 49 | def read_manifest(self, scr, fn): |
| 50 | manifest = [] |
| 51 | manifest_lines = file(fn).readlines() |
| 52 | manifest_lines = [x.strip() for x in manifest_lines] |
Scott Baker | c2a2fe9 | 2016-06-02 13:19:10 -0700 | [diff] [blame] | 53 | manifest_lines = [x for x in manifest_lines if x] |
Scott Baker | b0eb23e | 2016-06-01 16:08:04 -0700 | [diff] [blame] | 54 | for line in manifest_lines: |
Scott Baker | c2a2fe9 | 2016-06-02 13:19:10 -0700 | [diff] [blame] | 55 | url_parts = urlparse.urlsplit(scr.full_url) |
| 56 | new_path = os.path.join(os.path.join(*os.path.split(url_parts.path)[:-1]),line) |
| 57 | url = urlparse.urlunsplit( (url_parts.scheme, url_parts.netloc, new_path, url_parts.query, url_parts.fragment) ) |
Scott Baker | b0eb23e | 2016-06-01 16:08:04 -0700 | [diff] [blame] | 58 | |
| 59 | build_fn = os.path.join(self.get_dest_dir(scr), line) |
Scott Baker | c2a2fe9 | 2016-06-02 13:19:10 -0700 | [diff] [blame] | 60 | download_fn = os.path.join(self.build_dir, build_fn) |
Scott Baker | b0eb23e | 2016-06-01 16:08:04 -0700 | [diff] [blame] | 61 | |
| 62 | manifest.append( (url, download_fn, build_fn) ) |
| 63 | return manifest |
| 64 | |
| 65 | def download_file(self, url, dest_fn): |
| 66 | logger.info("Download %s to %s" % (url, dest_fn)) |
| 67 | if not os.path.exists(os.path.dirname(dest_fn)): |
| 68 | os.makedirs(os.path.dirname(dest_fn)) |
| 69 | obj = urllib2.urlopen(url) |
| 70 | file(dest_fn,"w").write(obj.read()) |
| 71 | |
Scott Baker | c2a2fe9 | 2016-06-02 13:19:10 -0700 | [diff] [blame] | 72 | # make python files executable |
| 73 | if dest_fn.endswith(".py"): # and contents.startswith("#!"): |
| 74 | os.chmod(dest_fn, 0755) |
| 75 | |
Scott Baker | b0eb23e | 2016-06-01 16:08:04 -0700 | [diff] [blame] | 76 | def download_resource(self, scr): |
| 77 | if scr.format == "manifest": |
| 78 | manifest_fn = self.get_download_fn(scr) |
| 79 | self.download_file(scr.full_url, manifest_fn) |
Scott Baker | c2a2fe9 | 2016-06-02 13:19:10 -0700 | [diff] [blame] | 80 | manifest = self.read_manifest(scr, manifest_fn) |
Scott Baker | b0eb23e | 2016-06-01 16:08:04 -0700 | [diff] [blame] | 81 | for (url, download_fn, build_fn) in manifest: |
| 82 | self.download_file(url, download_fn) |
| 83 | else: |
| 84 | self.download_file(scr.full_url, self.get_download_fn(scr)) |
| 85 | |
| 86 | def get_docker_lines(self, scr): |
| 87 | if scr.format == "manifest": |
| 88 | manifest_fn = self.get_download_fn(scr) |
| 89 | manifest = self.read_manifest(scr, manifest_fn) |
| 90 | lines = [] |
| 91 | for (url, download_fn, build_fn) in manifest: |
| 92 | lines.append("ADD %s /%s" % (build_fn, build_fn)) |
| 93 | return lines |
| 94 | else: |
| 95 | build_fn = self.get_build_fn(scr) |
| 96 | return ["ADD %s /%s" % (build_fn, build_fn)] |
| 97 | |
| 98 | def get_controller_docker_lines(self, controller, kinds): |
| 99 | dockerfile=[] |
| 100 | for scr in controller.service_controller_resources.all(): |
| 101 | if scr.kind in kinds: |
| 102 | lines = self.get_docker_lines(scr) |
| 103 | dockerfile = dockerfile + lines |
| 104 | return dockerfile |
| 105 | |
| 106 | # stuff that has to do with building |
| 107 | |
Scott Baker | 513ea45 | 2016-06-02 16:03:26 -0700 | [diff] [blame] | 108 | def create_xos_app_data(self, name, dockerfile, app_list, migration_list): |
Scott Baker | ae196c3 | 2016-06-01 23:29:22 -0700 | [diff] [blame] | 109 | if not os.path.exists(os.path.join(self.build_dir,"opt/xos/xos")): |
| 110 | os.makedirs(os.path.join(self.build_dir,"opt/xos/xos")) |
| 111 | |
| 112 | if app_list: |
Scott Baker | 513ea45 | 2016-06-02 16:03:26 -0700 | [diff] [blame] | 113 | dockerfile.append("COPY opt/xos/xos/%s_xosbuilder_app_list /opt/xos/xos/xosbuilder_app_list" % name) |
| 114 | file(os.path.join(self.build_dir, "opt/xos/xos/%s_xosbuilder_app_list") % name, "w").write("\n".join(app_list)+"\n") |
Scott Baker | ae196c3 | 2016-06-01 23:29:22 -0700 | [diff] [blame] | 115 | |
| 116 | if migration_list: |
Scott Baker | 513ea45 | 2016-06-02 16:03:26 -0700 | [diff] [blame] | 117 | dockerfile.append("COPY opt/xos/xos/%s_xosbuilder_migration_list /opt/xos/xos/xosbuilder_migration_list" % name) |
| 118 | file(os.path.join(self.build_dir, "opt/xos/xos/%s_xosbuilder_migration_list") % name, "w").write("\n".join(migration_list)+"\n") |
Scott Baker | ae196c3 | 2016-06-01 23:29:22 -0700 | [diff] [blame] | 119 | |
Scott Baker | b0eb23e | 2016-06-01 16:08:04 -0700 | [diff] [blame] | 120 | def create_ui_dockerfile(self): |
| 121 | dockerfile_fn = "Dockerfile.UI" |
| 122 | |
Scott Baker | ae196c3 | 2016-06-01 23:29:22 -0700 | [diff] [blame] | 123 | app_list = [] |
| 124 | migration_list = [] |
| 125 | |
Scott Baker | b0eb23e | 2016-06-01 16:08:04 -0700 | [diff] [blame] | 126 | dockerfile = ["FROM %s" % self.source_ui_image] |
| 127 | for controller in ServiceController.objects.all(): |
| 128 | dockerfile = dockerfile + self.get_controller_docker_lines(controller, self.UI_KINDS) |
Scott Baker | ae196c3 | 2016-06-01 23:29:22 -0700 | [diff] [blame] | 129 | if controller.service_controller_resources.filter(kind="models").exists(): |
| 130 | app_list.append("services." + controller.name) |
| 131 | migration_list.append(controller.name) |
| 132 | |
Scott Baker | 513ea45 | 2016-06-02 16:03:26 -0700 | [diff] [blame] | 133 | self.create_xos_app_data("ui", dockerfile, app_list, migration_list) |
Scott Baker | b0eb23e | 2016-06-01 16:08:04 -0700 | [diff] [blame] | 134 | |
| 135 | file(os.path.join(self.build_dir, dockerfile_fn), "w").write("\n".join(dockerfile)+"\n") |
| 136 | |
Scott Baker | 8d252b6 | 2016-06-01 23:08:04 -0700 | [diff] [blame] | 137 | return {"dockerfile_fn": dockerfile_fn, |
| 138 | "docker_image_name": "xosproject/xos-ui"} |
Scott Baker | b0eb23e | 2016-06-01 16:08:04 -0700 | [diff] [blame] | 139 | |
| 140 | def create_synchronizer_dockerfile(self, controller): |
Scott Baker | 513ea45 | 2016-06-02 16:03:26 -0700 | [diff] [blame] | 141 | # bake in the synchronizer from this controller |
| 142 | sync_lines = self.get_controller_docker_lines(controller, self.SYNC_CONTROLLER_KINDS) |
| 143 | if not sync_lines: |
Scott Baker | b0eb23e | 2016-06-01 16:08:04 -0700 | [diff] [blame] | 144 | return [] |
| 145 | |
| 146 | dockerfile_fn = "Dockerfile.%s" % controller.name |
| 147 | dockerfile = ["FROM %s" % self.source_sync_image] |
Scott Baker | 513ea45 | 2016-06-02 16:03:26 -0700 | [diff] [blame] | 148 | |
| 149 | # Now bake in models from this controller as well as the others |
| 150 | # It's important to bake all services in, because some services' |
| 151 | # synchronizers may depend on models from another service. |
| 152 | app_list = [] |
| 153 | for c in ServiceController.objects.all(): |
| 154 | dockerfile = dockerfile + self.get_controller_docker_lines(c, self.SYNC_ALLCONTROLLER_KINDS) |
| 155 | if controller.service_controller_resources.filter(kind="models").exists(): |
| 156 | app_list.append("services." + controller.name) |
| 157 | |
| 158 | self.create_xos_app_data(controller.name, dockerfile, app_list, None) |
| 159 | |
| 160 | dockerfile = dockerfile + sync_lines |
Scott Baker | c2a2fe9 | 2016-06-02 13:19:10 -0700 | [diff] [blame] | 161 | file(os.path.join(self.build_dir, dockerfile_fn), "w").write("\n".join(dockerfile)+"\n") |
Scott Baker | b0eb23e | 2016-06-01 16:08:04 -0700 | [diff] [blame] | 162 | |
Scott Baker | 8d252b6 | 2016-06-01 23:08:04 -0700 | [diff] [blame] | 163 | return {"dockerfile_fn": dockerfile_fn, |
| 164 | "docker_image_name": "xosproject/xos-synchronizer-%s" % controller.name} |
Scott Baker | b0eb23e | 2016-06-01 16:08:04 -0700 | [diff] [blame] | 165 | |
Scott Baker | 8d252b6 | 2016-06-01 23:08:04 -0700 | [diff] [blame] | 166 | # def build_xos(self): |
| 167 | # dockerfiles=[] |
| 168 | # dockerfiles.append(self.create_ui_dockerfile()) |
| 169 | # |
| 170 | # for controller in ServiceController.objects.all(): |
| 171 | # dockerfiles.append(self.create_synchronizer_dockerfile(controller)) |
Scott Baker | b0eb23e | 2016-06-01 16:08:04 -0700 | [diff] [blame] | 172 | |
| 173 | |
| 174 | |