Example #1
0
def test_api_object():
    pod = Pod(None, {'metadata': {'name': 'myname'}})
    assert repr(pod) == '<Pod myname>'
    assert str(pod) == 'myname'
    assert pod.metadata == {'name': 'myname'}
    assert pod.labels == {}
    assert pod.annotations == {}
Example #2
0
    def __produce_log_file(self, job_state):
        pod_r = Pod.objects(self._pykube_api).filter(selector="app=" +
                                                     job_state.job_id)
        log_string = ""
        for pod_obj in pod_r.response['items']:
            try:
                pod = Pod(self._pykube_api, pod_obj)
                log_string += "\n\n==== Pod " + pod.name + " log start ====\n\n"
                log_string += pod.logs(timestamps=True)
                log_string += "\n\n==== Pod " + pod.name + " log end   ===="
            except Exception as detail:
                log.info(
                    "Could not write log file for pod %s due to HTTPError %s",
                    pod_obj['metadata']['name'], detail)
        if isinstance(log_string, text_type):
            log_string = log_string.encode('utf8')

        logs_file_path = job_state.output_file
        try:
            with open(logs_file_path, mode="w") as logs_file:
                logs_file.write(log_string)
        except IOError as e:
            log.error("Couldn't produce log files for %s", job_state.job_id)
            log.exception(e)

        return logs_file_path
Example #3
0
def test_api_object():
    pod = Pod(None, {"metadata": {"name": "myname"}})
    assert repr(pod) == "<Pod myname>"
    assert str(pod) == "myname"
    assert pod.metadata == {"name": "myname"}
    assert pod.labels == {}
    assert pod.annotations == {}
Example #4
0
    def __get_pods(self):

        self.__logger.info(f"checking __get_pods")
        pod_objs = Pod.objects(self.__kube_api) \
            .filter(namespace=self.job_namespace, selector="job-name=" + self.uu_name) \
            .response['items']
        return [Pod(self.__kube_api, p) for p in pod_objs]
    def build_config_pod(self, os_version=""):
        if len(os_version) == 0:
            os_version = self.os_version

        print("Creating config generation pod...")
        return Pod(self.api,
        {
            "metadata":
            {
                "name": "generate-config",
                "labels":
                {
                    "purpose": "generate-config"
                },
                "namespace": "openshift-deploy"
            },
            "spec":
            {
                "containers":
                [{
                    "name": "generate-config",
                    "image": "openshift/origin:" + os_version,
                    "imagePullPolicy": "Always",
                    "command": ["/bin/bash"],
                    "args": ["/etc/config_secret_script/create-config.sh"],
                    "ports": [],
                    "env":
                    [{
                        "name": "OPENSHIFT_INTERNAL_ADDRESS",
                        "value": "https://" + self.os_internal_ip
                    }, {
                        "name": "OPENSHIFT_EXTERNAL_ADDRESS",
                        "value": "https://" + self.os_external_ip
                    }, {
                        "name": "ETCD_ADDRESS",
                        "value": "http://etcd:4001"
                    }],
                    "volumeMounts":
                    [{
                        "mountPath": "/etc/config_secret_script",
                        "name": "config-secret-script",
                        "readOnly": True
                    }, {
                        "mountPath": "/etc/kube_config",
                        "name": "kube-config",
                        "readOnly": True
                    }]
                }],
                "volumes":
                [{
                    "name": "config-secret-script",
                    "secret": {"secretName": "create-config-script"}
                }, {
                    "name": "kube-config",
                    "secret": {"secretName": "kubeconfig"}
                }],
                "restartPolicy": "Never"
            }
        })
Example #6
0
 def get_mongo_pods():
     return [
         Pod(
             None, {
                 'metadata': {
                     'labels': {
                         'hostname': 'fb-1.db.waverbase.com:%d' % p
                     }
                 },
                 'status': {
                     'podIP': '127.0.0.1:%d' % p
                 }
             }) for p in range(base, base + num)
     ]
Example #7
0
    def __job_failed_due_to_low_memory(self, job_state):
        """
        checks the state of the pod to see if it was killed
        for being out of memory (pod status OOMKilled). If that is the case
        marks the job for resubmission (resubmit logic is part of destinations).
        """

        pods = Pod.objects(self._pykube_api).filter(selector="app=" + job_state.job_id)
        pod = Pod(self._pykube_api, pods.response['items'][0])

        if pod.obj['status']['phase'] == "Failed" and \
                pod.obj['status']['containerStatuses'][0]['state']['terminated']['reason'] == "OOMKilled":
            return True

        return False
Example #8
0
def test_update():
    pod = Pod(
        None, {
            'metadata': {
                'name': 'john',
                'kind': 'test'
            },
            'annotations': 'a long string'
        })
    pod.obj = {'metadata': {'name': 'john'}}
    pod.api = MagicMock()
    pod.api.patch.return_value.json.return_value = obj_merge(
        pod.obj, pod._original_obj, False)
    pod.update(is_strategic=False)
    assert pod.metadata == {'name': 'john'}
    assert pod.annotations == {}
Example #9
0
def test_map_empty_pod():
    pod = Pod(None, {"metadata": {}, "spec": {"containers": []}, "status": {}})
    assert map_pod(pod, 0, 0) == {
        "application": "",
        "component": "",
        "container_images": [],
        "team": "",
        "requests": {
            "cpu": 0,
            "memory": 0
        },
        "usage": {
            "cpu": 0,
            "memory": 0
        },
        "cost": 0,
    }
Example #10
0
    def run(self):
        self._init_kubernetes()
        self.log_event(
            api.LogEntry(
                self.run_id, api.TaskStatus.RUNNING,
                "Starting task: {}.".format(self.task_family) + self.uu_name,
                self.__repr__(), self.uu_name))
        command = self.build_command()
        full_command = []
        full_command.extend(self.args)
        if command:
            full_command.append(command)
        self.log_event(
            api.LogEntry(
                self.run_id, api.TaskStatus.RUNNING,
                "{} task command: ".format(self.task_family) +
                ' '.join(full_command), self.__repr__(), self.uu_name))
        # Render pod
        pod_json = self.create_pod(full_command)
        # Update user labels
        pod_json['metadata']['labels'].update(self.labels)

        if self.autoscaling_enabled:
            pod_json['metadata']['labels'].update({"runid": self.run_id})
            pod_json['spec']['nodeSelector'].update({"runid": self.run_id})

        pod = Pod(self.__kube_api, pod_json)
        try:
            pod.create()
        except HTTPError as error:
            self.log_event(
                api.LogEntry(
                    self.run_id, api.TaskStatus.RUNNING,
                    "Failed to create Kubernetes pod: " + self.uu_name +
                    "; error: " + error.message, self.__repr__(),
                    self.uu_name))
            raise RuntimeError
        # Track the Job (wait while active)

        self.log_event(
            api.LogEntry(self.run_id, api.TaskStatus.RUNNING,
                         "Start tracking Kubernetes pod: " + self.uu_name,
                         self.__repr__(), self.uu_name))
        seen_events = set()
        self.__track_pod(seen_events)
        self.post_process()
Example #11
0
def test_update():
    pod = Pod(
        None,
        {
            "metadata": {
                "name": "john",
                "kind": "test"
            },
            "annotations": "a long string"
        },
    )
    pod.obj = {"metadata": {"name": "john"}}
    pod.api = MagicMock()
    pod.api.patch.return_value.json.return_value = obj_merge(
        pod.obj, pod._original_obj, False)
    pod.update(is_strategic=False)
    assert pod.metadata == {"name": "john"}
    assert pod.annotations == {}
Example #12
0
    def __get_pod_status(self):
        # Look for the required pod
        pods = Pod.objects(self.__kube_api).filter(selector="luigi_task_id=" +
                                                   self.job_uuid)
        # Raise an exception if no such pod found
        if len(pods.response["items"]) == 0:
            self.log_event(
                api.LogEntry(self.run_id, api.TaskStatus.FAILURE,
                             "Kubernetes pod failed to raise: " + self.uu_name,
                             self.__repr__(), self.uu_name))
            raise RuntimeError("Kubernetes job " + self.uu_name + " not found")

        # Figure out status and return it
        pod = Pod(self.__kube_api, pods.response["items"][0])
        if self.__SUCCESS_STATUS in pod.obj["status"]["phase"]:
            return self.__SUCCESS_STATUS
        if self.__FAILURE_STATUS in pod.obj["status"]["phase"]:
            return self.__FAILURE_STATUS
        return self.__RUNNING_STATUS
Example #13
0
def test_map_pod_with_resources():
    pod = Pod(
        None,
        {
            "metadata": {
                "labels": {
                    "app": "myapp",
                    "component": "mycomp",
                    "team": "myteam"
                }
            },
            "spec": {
                "containers": [{
                    "name": "main",
                    "image": "hjacobs/kube-downscaler:latest",
                    "resources": {
                        "requests": {
                            "cpu": "5m",
                            "memory": "200Mi"
                        }
                    },
                }]
            },
            "status": {},
        },
    )
    assert map_pod(pod, 0, 0) == {
        "application": "myapp",
        "component": "mycomp",
        "team": "myteam",
        "container_names": ["main"],
        "container_images": ["hjacobs/kube-downscaler:latest"],
        "requests": {
            "cpu": 0.005,
            "memory": 200 * 1024 * 1024
        },
        "usage": {
            "cpu": 0,
            "memory": 0
        },
        "cost": 0,
    }
Example #14
0
    def __job_failed_due_to_low_memory(self, job_state):
        """
        checks the state of the pod to see if it was killed
        for being out of memory (pod status OOMKilled). If that is the case
        marks the job for resubmission (resubmit logic is part of destinations).
        """

        pods = Pod.objects(self._pykube_api).filter(selector="app=" +
                                                    job_state.job_id)
        if len(pods.response['items']) == 0 or pods is None:
            log.error("Cannot find API server or app={} doesn't find any pods".
                      format(str(job_state.job_id)))
            return False
        pod = Pod(self._pykube_api, pods.response['items'][0])

        if pod.obj['status']['phase'] == "Failed" and \
                pod.obj['status']['containerStatuses'][0]['state']['terminated']['reason'] == "OOMKilled":
            return True

        return False
Example #15
0
    def __produce_log_file(self, job_state):
        pod_r = Pod.objects(self._pykube_api).filter(selector="app=" +
                                                     job_state.job_id)
        logs = ""
        for pod_obj in pod_r.response['items']:
            try:
                pod = Pod(self._pykube_api, pod_obj)
                logs += "\n\n==== Pod " + pod.name + " log start ====\n\n"
                logs += pod.logs(timestamps=True)
                logs += "\n\n==== Pod " + pod.name + " log end   ===="
            except Exception as detail:
                log.info("Could not write pod\'s " +
                         pod_obj['metadata']['name'] +
                         " log file due to HTTPError " + str(detail))

        logs_file_path = job_state.output_file
        logs_file = open(logs_file_path, mode="w")
        if isinstance(logs, text_type):
            logs = logs.encode('utf8')
        logs_file.write(logs)
        logs_file.close()
        return logs_file_path
    def build_execute_pod(self, command, admin_conf, os_version=""):
        if len(os_version) == 0:
            os_version = self.os_version

        print("Creating command execution pod...")
        name = "oskube-execute-" + random_string(4).lower()
        command = "mkdir -p ~/.kube/ && echo \"$ADMIN_KUBECONFIG\" > ~/.kube/config && " + command
        return Pod(self.api,
        {
            "metadata":
            {
                "name": name,
                "labels":
                {
                    "purpose": "exec-command"
                },
                "namespace": "openshift-origin"
            },
            "spec":
            {
                "containers":
                [{
                    "name": "exec-command",
                    "image": "openshift/origin:" + os_version,
                    "imagePullPolicy": "Always",
                    "command": ["/bin/bash"],
                    "args": ["-c", command],
                    "ports": [],
                    "env":
                    [{
                        "name": "ADMIN_KUBECONFIG",
                        "value": admin_conf
                    }]
                }],
                "restartPolicy": "Never"
            }
        })
Example #17
0
def editconfig(ctx):
    """Interactively edits master-config.yaml"""
    ctx.temp_dir = tempfile.mkdtemp()
    if ctx.auto_confirm:
        print(
            "Note: -y option is not supported for purely interactive commands."
        )
        ctx.auto_confirm = False

    if not ctx.init_with_checks():
        print("Failed cursory checks, exiting.")
        exit(1)

    if not ctx.consider_openshift_deployed:
        print(
            "I think OpenShift is not yet deployed. Use deploy first to create it."
        )
        exit(1)

    old_secret = ctx.fetch_config_to_dir(ctx.temp_dir)
    mc_path = ctx.temp_dir + "/master-config.yaml"
    if not os.path.exists(mc_path):
        print(
            "Fetched config files but they don't contain master-config.yaml, something's wrong. Try getconfig."
        )
        shutil.rmtree(ctx.temp_dir)
        exit(1)

    last_mtime = os.path.getmtime(mc_path)
    print("Config files are at: " + ctx.temp_dir)
    print("Feel free to edit as you will...")
    print("Launching editor...")
    call([EDITOR, mc_path])
    now_mtime = os.path.getmtime(mc_path)
    if now_mtime == last_mtime:
        print("No changes made, exiting.")
        shutil.rmtree(ctx.temp_dir)
        exit(0)

    if not click.confirm("Do you want to upload the changed config files?"):
        print("Okay, cancelling.")
        shutil.rmtree(ctx.temp_dir)
        exit(0)

    print("Preparing to upload config files...")
    # Serialize the config to a secret
    openshift_config_kv = {}
    for filen in os.listdir(ctx.temp_dir):
        with open(ctx.temp_dir + "/" + filen, 'rb') as f:
            openshift_config_kv[filen] = f.read()
    openshift_config_secret = ctx.build_secret("openshift-config",
                                               "openshift-origin",
                                               openshift_config_kv)
    openshift_config_secret._original_obj = old_secret.obj

    print("Attempting to patch secret...")
    openshift_config_secret.update()
    print("Updates applied.")

    if not click.confirm(
            "Do you want to restart openshift to apply the changes?"):
        print("Okay, I'm done. Have a nice day!")
        shutil.rmtree(ctx.temp_dir)
        exit(0)

    print("Restarting openshift pod...")
    try:
        pods = Pod.objects(ctx.api).filter(namespace="openshift-origin",
                                           selector={
                                               "app": "openshift"
                                           }).response["items"]
        if len(pods) >= 1:
            openshift_pod = Pod(ctx.api, pods[0])
            print("Deleting pod " + openshift_pod.obj["metadata"]["name"] +
                  "...")
            openshift_pod.delete()
    except:
        print(
            "Something went wrong restarting openshift, do it yourself please!"
        )

    shutil.rmtree(ctx.temp_dir)
Example #18
0
 def __get_pods(self):
     pod_objs = Pod.objects(self.__kube_api, namespace=self.kubernetes_namespace) \
         .filter(selector="job-name=" + self.uu_name) \
         .response['items']
     return [Pod(self.__kube_api, p) for p in pod_objs]
Example #19
0
def test_set_label():
    pod = Pod(None, {'metadata': {'name': 'myname'}})
    pod.labels['foo'] = 'bar'
    assert pod.labels['foo'] == 'bar'
Example #20
0
def test_set_annotation():
    pod = Pod(None, {'metadata': {'name': 'myname'}})
    pod.annotations['foo'] = 'bar'
    assert pod.annotations['foo'] == 'bar'
Example #21
0
def test_set_annotation():
    pod = Pod(None, {"metadata": {"name": "myname"}})
    pod.annotations["foo"] = "bar"
    assert pod.annotations["foo"] == "bar"
Example #22
0
def test_set_label():
    pod = Pod(None, {"metadata": {"name": "myname"}})
    pod.labels["foo"] = "bar"
    assert pod.labels["foo"] == "bar"
Example #23
0
def deploy(ctx, persistent_volume, load_balancer, public_hostname,
           create_volume, master_config_override, server_key):
    """Deploy OpenShift to the cluster."""
    if not load_balancer and public_hostname != None:
        print(
            "You must specify --load-balancer with --public-hostname, I can't map a public hostname without a load balancer."
        )
        exit(1)

    if not ctx.init_with_checks():
        print("Failed cursory checks, exiting.")
        exit(1)

    if ctx.consider_openshift_deployed:
        print(
            "I think OpenShift is already deployed. Use undeploy first to remove it before installing it again."
        )
        print(
            "Consider if you really need a full redeploy. You can update without re-deploying!"
        )
        exit(1)

    print()
    if "openshift" in ctx.namespace_names or "openshift-origin" in ctx.namespace_names:
        print(
            "The namespaces 'openshift' and/or 'openshift-origin' exist, this indicates a potentially existing/broken install."
        )
        if ctx.auto_confirm:
            print(
                "Auto confirm (-y) option set, clearing existing installation."
            )
        else:
            print("Really consider the decision you're about to make.")
            if not click.confirm(
                    "Do you want to clear the existing installation?"):
                print("Okay, cancelling.")
                exit(1)

        # Handle oddities with finalizers?
        # todo: delete "openshift" and "openshift-infra" namespaces
        ctx.delete_namespace_byname("openshift-origin")
        time.sleep(1)

    ctx.temp_dir = tempfile.mkdtemp()
    print("Preparing to execute deploy...")
    print("Deploy temp dir: " + ctx.temp_dir)

    # Setup the deploy state namespace
    ctx.cleanup_osdeploy_namespace()
    ctx.create_osdeploy_namespace()

    # Check the persistentvolume exists
    if not create_volume and ctx.find_persistentvolume(
            persistent_volume) == None:
        print(" [!] persistentvolume with name " + persistent_volume +
              " does not exist. Did you create it?")
        exit(1)

    # Create the namespaces
    ctx.create_namespace("openshift-origin")

    # Create the service
    if load_balancer:
        print("Will use load balancer type service.")
    else:
        print("Will use node port type service.")
    os_service = ctx.create_os_service(load_balancer)

    # Wait for it to be ready if it's a load balancer
    if load_balancer:
        print("Waiting for service load balancer IP to be allocated...")
        ctx.wait_for_loadbalancer(os_service)
    else:
        os_service.reload()

    internal_os_ip = os_service.obj["spec"]["clusterIP"]
    ctx.os_internal_ip = internal_os_ip

    if load_balancer:
        tmp = os_service.obj["status"]["loadBalancer"]["ingress"][0]
        external_os_ip = None
        external_is_hostname = False
        if "hostname" in tmp:
            external_os_ip = tmp["hostname"]
            external_is_hostname = True
        else:
            external_os_ip = tmp["ip"]
        ctx.os_external_ip = external_os_ip
        print("External OpenShift IP: " + external_os_ip)
    else:
        external_os_ip = internal_os_ip
        print("External OpenShift IP: nodes (node port)")

    print("Internal OpenShift IP: " + internal_os_ip)

    if public_hostname != None:
        print("You need to DNS map like this:")
        if external_is_hostname:
            print(public_hostname + ".\t300\tIN\tCNAME\t" + external_os_ip)
        else:
            print(public_hostname + ".\t300\tIN\tA\t" + external_os_ip)
        ctx.os_external_ip = public_hostname

    # Create a 'secret' containing the script to run to config.
    create_config_script = (resource_string(ctx.scripts_resource,
                                            'create-config.sh'))

    # Build the secret
    create_config_secret_kv = {"create-config.sh": create_config_script}
    create_config_secret = ctx.build_secret("create-config-script",
                                            "openshift-deploy",
                                            create_config_secret_kv)
    create_config_secret.create()

    # Build the kubeconfig secret
    kubeconfig_secret_kv = {
        "kubeconfig": yaml.dump(ctx.config.doc).encode('ascii')
    }
    kubeconfig_secret = ctx.build_secret("kubeconfig", "openshift-deploy",
                                         kubeconfig_secret_kv)
    kubeconfig_secret.create()

    # Generate the openshift config by running a temporary pod on the cluster
    print("Generating openshift config via cluster...")
    conf_pod = ctx.build_config_pod(ctx.os_version)
    conf_pod.create()
    with open(ctx.temp_dir + "/config_bundle.tar.gz", 'wb') as f:
        conf_bundle = ctx.observe_config_pod(conf_pod)
        conf_bundle_data = base64.b64decode(conf_bundle)
        f.write(conf_bundle_data)
    conf_pod.delete()

    # Extract
    tar = tarfile.open(ctx.temp_dir + "/config_bundle.tar.gz")
    tar.extractall(ctx.temp_dir + "/config/")
    tar.close()

    # Move kubeconfig in
    with open(ctx.temp_dir + "/config/external-master.kubeconfig", 'w') as f:
        f.write(yaml.dump(ctx.config.doc))

    # Delete tarfile
    os.remove(ctx.temp_dir + "/config_bundle.tar.gz")

    # Do some processing on the master-config yaml
    conf = None
    with open(ctx.temp_dir + '/config/master-config.yaml') as f:
        conf = yaml.load(f)
    conf = ctx.fix_master_config(conf)

    # Write the serviceaccounts file again
    with open(server_key, 'r') as fs:
        with open(ctx.temp_dir + "/config/serviceaccounts.public.key",
                  'w') as fd:
            fd.write(fs.read())

    # Load patches if needed
    master_config_override_kv = None
    if master_config_override != None:
        print("Loading " + master_config_override + "...")
        with open(master_config_override, 'r') as f:
            master_config_override_kv = yaml.load(f)
        conf = deepupdate(conf, master_config_override_kv)

    # Write the fixed master config
    with open(ctx.temp_dir + "/config/master-config.yaml", 'w') as f:
        f.write(yaml.dump(conf, default_flow_style=False))

    # Allow the user to edit the openshift config last second
    print("Generated updated master-config.yaml.")
    if ctx.auto_confirm:
        print(
            "Auto confirm (-y) option set, skipping master-config.yaml edit opportunity."
        )
    else:
        if click.confirm("Do you want to edit master-config.yaml?"):
            call([EDITOR, ctx.temp_dir + "/config/master-config.yaml"])

    # Cleanup a bit
    kubeconfig_secret.delete()
    create_config_secret.delete()

    # Serialize the config to a secret
    openshift_config_kv = {}
    for filen in os.listdir(ctx.temp_dir + "/config"):
        with open(ctx.temp_dir + "/config/" + filen, 'rb') as f:
            openshift_config_kv[filen] = f.read()
    openshift_config_secret = ctx.build_secret("openshift-config",
                                               "openshift-origin",
                                               openshift_config_kv)

    # Save the secret
    openshift_config_secret.create()

    # Starting etcd setup... build PersistentVolumeClaim
    etcd_pvc = ctx.build_pvc("openshift-etcd1", "openshift-origin", "2Gi",
                             create_volume)
    etcd_pvc.create()

    # Create the etcd controller
    etcd_rc = ctx.build_etcd_rc("openshift-etcd1")
    etcd_svc = ctx.build_etcd_service()

    print("Creating etcd service...")
    etcd_svc.create()

    print("Creating etcd controller...")
    etcd_rc.create()

    print("Waiting for etcd pod to be created...")
    etcd_pod = None
    # Wait for the pod to exist
    while etcd_pod == None:
        etcd_pods = Pod.objects(ctx.api).filter(
            selector={
                "app": "etcd"
            }, namespace="openshift-origin").response["items"]
        if len(etcd_pods) < 1:
            time.sleep(0.5)
            continue
        etcd_pod = Pod(ctx.api, etcd_pods[0])

    # Wait for it to run
    ctx.wait_for_pod_running(etcd_pod)

    # Create the controller config
    print("Creating openshift replication controller...")
    openshift_rc = ctx.build_openshift_rc(ctx.os_version)
    openshift_rc.create()

    print("Waiting for openshift pod to be created...")
    openshift_pod = None
    # Wait for the pod to exist
    while openshift_pod == None:
        pods = Pod.objects(ctx.api).filter(namespace="openshift-origin",
                                           selector={
                                               "app": "openshift"
                                           }).response["items"]
        if len(pods) < 1:
            time.sleep(0.5)
            continue
        openshift_pod = Pod(ctx.api, pods[0])

    # Wait for it to run
    ctx.wait_for_pod_running(openshift_pod)

    print()
    print(" == OpenShift Deployed ==")
    print("External IP: " + ctx.os_external_ip)

    ctx.fetch_namespaces()
    ctx.cleanup_osdeploy_namespace()
    shutil.rmtree(ctx.temp_dir)