def __init__(self, config_file): import uuid import kubernetes super(KubernetesContainer, self).__init__() if "KUBERNETES_SERVICE_PORT" in os.environ: kubernetes.config.load_incluster_config() else: kubernetes.config.load_kube_config() self.env_ = [] self.secrets_ = [] self.config_file_ = config_file self.apps_api_ = kubernetes.client.AppsV1Api() self.core_api_ = kubernetes.client.CoreV1Api() self.ext_api_ = kubernetes.client.ApiextensionsV1beta1Api() self.obj_api_ = kubernetes.client.CustomObjectsApi() self.namespace_ = Environment.get("kubernetes.namespace") self.port_ = None self.deploymentName_ = "ibm-app-gw-{0}".format(uuid.uuid1()) self.configmapName_ = "ibm-app-gw.config.{0}".format(uuid.uuid1()) self.useCRD_ = self.useCRD() logger.info( "Using a kubernetes public IP of {0}. Use the " "kubernetes.ip configuration entry to change the IP to suit " "your environment.".format(Environment.get("kubernetes.ip")))
def __init__(self, config_file=None): """ Initialize this class. Note that a VersionException will be raised if the version number contained within the configuration file is greater than the version number of the requested IBM Application Gateway image. """ super(Container, self).__init__() # If a configuration file is specified we need to ensure that the # IAG version supports the version of the specified configuration # file. if config_file is not None: with open(config_file, 'r') as stream: data = yaml.safe_load(stream) if data['version'] > Environment().get("image.tag"): raise VersionException( "The configuration file is not supported " "with the specified image version: {0}".format( Environment().get("image.tag"))) if run_kubernetes(): self.client_ = KubernetesContainer(config_file) else: self.client_ = DockerContainer(config_file=config_file)
def startContainer(self, image): """ The following command is used to start the IBM Application Gateway container using the supplied configuration. """ if self.container_ is not None: logger.critical( "A container has already been started in this object.") raise Exception( "A container has already been started in this object.") volumes = [] if Environment.is_container_context(): volumes = { Environment.get("docker.volume_name"): { "bind": Container.config_volume_path, "mode": "rw" } } elif self.cfgFile_ is not None: volumes = [ "{0}:{1}/config.yaml".format(self.cfgFile_, Container.config_volume_path) ] self.container_ = self.client_.containers.run(image, auto_remove=True, environment=self.env_, detach=True, publish_all_ports=True, volumes=volumes)
def write(self, filename=None): """ Write the current configuration as a yaml file which can be used by the container. @param filename : The name of the file to write. If no file is specified a temporary file will be created. @retval The name of the file which has been written. """ data = self.toYaml() # If we have not been provided with a file name we create a file name # now. if filename is None: dir = Container.config_volume_path \ if Environment.is_container_context() else None fd, filename = mkstemp(suffix=".yml", dir=dir) if Environment.is_container_context(): os.fchown(fd, Environment.iag_user, Environment.iag_group) os.close(fd) # Write the data. with open(filename, 'w') as outfile: yaml.dump(data, outfile, width=float("inf")) logger.info("Wrote the IBM Application Gateway configuration to {0}".\ format(filename)) return filename
def startContainer(self, removeAtExit=True, protocol="https"): """ The following command is used to start the IBM Application Gateway container using the supplied configuration. removeAtExit: should the container be automatically removed when the script exits? """ image = "{0}:{1}".format(Environment().get("image.name"), Environment().get("image.tag")) logger.info("Starting the container from {0}".format(image)) logger.info("Protocol to start {0}".format(protocol)) self.client_.startContainer(image) if removeAtExit: atexit.register(self.stopContainer) # Wait for the container to become healthy. We should really be using # the health of the container, but this takes a while to kick in. So, # we instead poll the https port of the server until we make a # successful SSL connection. running = False attempt = 0 while not running and attempt < 30: time.sleep(1) try: logger.info("Protocol in loop {0}".format(protocol)) logger.info("Port in loop {0}".format(self.port(protocol))) logger.info("IP in loop {0}".format(self.ipaddr())) requests.get("{0}://{1}:{2}".format(protocol, self.ipaddr(), self.port(protocol)), verify=False, allow_redirects=False, timeout=2) running = True except: self.client_.reload() attempt += 1 if not running: message = "The container failed to start within the allocated time." logger.critical(message) logger.critical(self.client_.container_.logs()) raise Exception(message) logger.info("The container has started")
def ipaddr(self, protocol="https"): """ Retrieve the IP address which can be used to access the container. """ if self.container_ is None: logger.critical("Error> the container is not currently running!") raise Exception("The container is not currently running!") if protocol == "https": port = "8443/tcp" elif protocol == "http": port = "8080/tcp" else: logger.critical("Error> an invalid protocol was specified!") raise Exception("An invalid protocol was specified!") ipaddr = self.container_.attrs['NetworkSettings']['Ports'][port]\ [0]['HostIp'] if ipaddr == "0.0.0.0": ipaddr = Environment.get("docker.ip") return ipaddr
def ipaddr(self, protocol="https"): """ Retrieve the IP address which can be used to access the container. """ if self.port_ is None: logger.critical("Error> the container is not currently running!") raise Exception("The container is not currently running!") return self.deploymentName_ if "KUBERNETES_SERVICE_PORT" in os.environ \ else Environment.get("kubernetes.ip")
def __createDeploymentObject(self, image): """ Create the deployment object for the IBM Application Gateway. """ import kubernetes # If a configuration file has been specified we want to mount the # configmap. volumes = [] volume_mounts = [] if self.config_file_ is not None: if self.useCRD_: self.setEnv("CONFIG_CUSTOM_OBJECT_NAME", self.configmapName_) else: volumes.append( kubernetes.client.V1Volume( config_map=kubernetes.client.V1ConfigMapVolumeSource( items=[ kubernetes.client.V1KeyToPath( path="config.yaml", key=self.__config_map_key) ], name=self.configmapName_), name=self.deploymentName_)) volume_mounts.append( kubernetes.client.V1VolumeMount( mount_path=Container.config_volume_path, name=self.deploymentName_)) # Create the pod template container container = kubernetes.client.V1Container( name=self.deploymentName_, image=image, ports=[ kubernetes.client.V1ContainerPort(container_port=8443, name="https"), kubernetes.client.V1ContainerPort(container_port=8080, name="http") ], env=self.env_, volume_mounts=volume_mounts, env_from=self.secrets_) # Create the secret which is used when pulling the IAG image. secret = kubernetes.client.V1LocalObjectReference( name=Environment.get("kubernetes.image_pull_secret")) # Create and configurate a spec section template = kubernetes.client.V1PodTemplateSpec( metadata=kubernetes.client.V1ObjectMeta( labels={"app": self.deploymentName_}), spec=kubernetes.client.V1PodSpec(containers=[container], image_pull_secrets=[secret], volumes=volumes)) # Create the specification of the deployment spec = kubernetes.client.V1DeploymentSpec( replicas=1, template=template, selector={'matchLabels': { 'app': self.deploymentName_ }}) # Instantiate the deployment object deployment = kubernetes.client.V1Deployment( api_version="apps/v1", kind="Deployment", metadata=kubernetes.client.V1ObjectMeta(name=self.deploymentName_), spec=spec) return deployment