def _ensureResource(self, action, resource_type): # Check input args assert action in Service.VALID_ACTIONS assert resource_type in Service.VALID_RESOURCE_TYPES assert resource_type in self.resourceTemplates resourceTemplates = self.resourceTemplates[resource_type] # If action is delete, then delete the resourceTemplates in # reverse order. if action == 'delete': resourceTemplates = reversed(resourceTemplates) for resourceTemplate in resourceTemplates: # Build the command based on if shell script or not. If # shell script, pipe to sh. Else, pipe to kubectl cmd = "kolla-kubernetes resource-template {} {} {}".format( action, resource_type, resourceTemplate.getName()) if resourceTemplate.getTemplatePath().endswith('.sh.j2'): cmd += " | sh" else: cmd += " | kubectl {} -f -".format(action) # Execute the command ExecUtils.exec_command(cmd)
def _ensureConfigMaps(self, action): assert action in Service.VALID_ACTIONS nsname = 'kolla_kubernetes_namespace' cmd = ("kubectl {} configmap {} --namespace={}".format( action, self.getName(), KollaKubernetesResources.GetJinjaDict()[nsname])) # For the create action, add some more arguments if action == 'create': for f in PathFinder.find_config_files(self.getName()): cmd += ' --from-file={}={}'.format( os.path.basename(f).replace("_", "-"), f) # Execute the command ExecUtils.exec_command(cmd)
def doCheck(self): # Build the templating command cmd = "kolla-kubernetes resource-template {} {} {}".format( 'create', self.resource_type, self.resource_template_obj.getName()) # Execute the command to get the processed template output template_out, err = ExecUtils.exec_command(cmd) # Skip templates which which produce no-ops (100% whitespace) # (e.g. pv templates for AWS should not use persistent # volumes because AWS uses experimental # auto-provisioning) if (err is not None): self.errors.append('error processing template file: {}'.format( str(err))) return elif re.match("^\s+$", template_out): msg = "template {} produced empty output (NO-OP)".format( self.resource_template_obj.getTemplatePath()) self.oks.append(msg) return # If the template output produces a stream of yaml documents # which are then piped to kubectl, then we will receive a # stream of reports separated by "\n\n". Split on "\n\n" and # process each result individually. The overall result is the # merged output. definitions = re.split("^---", template_out, re.MULTILINE) for definition in definitions: kr = KubeResourceYamlStatus(definition) self.kube_resources.append(kr)
def test_validate_templates(self): srcdir = os.environ['HELMDIR'] helmbin = os.environ['HELMBIN'] repodir = os.environ['REPODIR'] microdir = os.path.join(srcdir, "microservice") microservices = os.listdir(microdir) packages = [p for p in microservices if _isdir(microdir, p)] print("Working on:") for package in packages: print(" %s" % package) with open(os.path.join(microdir, package, 'Chart.yaml')) as stream: version = yaml.safe_load(stream)['version'] cmd = "%s template %s/%s-%s.tgz" % (helmbin, repodir, package, version) out, err = ExecUtils.exec_command(cmd) if err: raise err l = yaml.safe_load_all(out) for y in l: js = '[]' try: # If there is a beta init container, validate it is proper # json key = 'pod.beta.kubernetes.io/init-containers' js = y['spec']['template']['metadata']['annotations'][key] except KeyError: pass except TypeError as e: m = ("'NoneType' object has no attribute '__getitem__'", "'NoneType' object is not subscriptable") if e.args[0] not in m: raise json.loads(js) pod = None try: pod = y['spec']['template'] except Exception: pass if pod: self._validate_image_pull_policy(package, pod)
def take_action(self, args, skip_and_return=False): # Validate input arguments self.validate_args(args) multi = len(args.resource_name) != 1 multidoc = {'apiVersion': 'v1', 'kind': 'List', 'items': []} for resource_name in args.resource_name: service_name = KKR.getServiceNameByResourceTypeName( args.resource_type, resource_name) service = KKR.getServiceByName(service_name) rt = service.getResourceTemplateByTypeAndName( args.resource_type, resource_name) tmpargs = copy.deepcopy(vars(args)) tmpargs['resource_name'] = resource_name variables = KKR.GetJinjaDict(service_name, tmpargs, args.print_jinja_keys_regex) # Merge the template vars with the jinja vars before processing variables['kolla_kubernetes'].update( {"template": { "vars": rt.getVars() }}) # handle the debug option --print-jinja-vars if args.print_jinja_vars is True: print(YamlUtils.yaml_dict_to_string(variables), file=sys.stderr) if args.resource_type == 'configmap' and \ rt.getTemplate() == 'auto': nsname = 'kolla_kubernetes_namespace' cmd = "kubectl create configmap {} -o yaml --dry-run" cmd = cmd.format(resource_name) for f in PathFinder.find_config_files(resource_name): cmd += ' --from-file={}={}'.format( os.path.basename(f).replace("_", "-"), f) # Execute the command out, err = ExecUtils.exec_command(cmd) y = yaml.load(out) y['metadata']['namespace'] = variables[nsname] res = y else: # process the template raw_doc = JinjaUtils.render_jinja( variables, FileUtils.read_string_from_file(rt.getTemplatePath())) res = yaml.load(raw_doc) if args.debug_container is not None: y = res kind = y['kind'] if kind not in ('PetSet', 'Deployment', 'Job', 'DaemonSet', 'ReplicationController', 'Pod'): raise Exception("Template doesn't have containers.") pod = y if kind != 'Pod': pod = y['spec']['template'] alpha_init_containers = None annotation = 'pod.alpha.kubernetes.io/init-containers' if 'metadata' in pod and 'annotations' in pod['metadata'] and \ annotation in pod['metadata']['annotations']: j = json.loads(pod['metadata']['annotations'][annotation]) alpha_init_containers = {} for c in j: alpha_init_containers[c['name']] = c containers = {} for c in pod['spec']['containers']: containers[c['name']] = c for c in args.debug_container: found = False warn_msg = "WARNING: container [{}] already has a" + \ " command override." warn_msg = warn_msg.format(c) if alpha_init_containers and c in alpha_init_containers: if 'command' in alpha_init_containers[c]: print(warn_msg, file=sys.stderr) if 'args' in alpha_init_containers[c]: del alpha_init_containers[c]['args'] alpha_init_containers[c]['command'] = \ ['/bin/bash', '-c', 'while true; do sleep 1000; done'] found = True if c in containers: if 'command' in containers[c]: print(warn_msg, file=sys.stderr) if 'args' in containers[c]: del containers[c]['args'] containers[c]['command'] = \ ['/bin/bash', '-c', 'while true; do sleep 1000; done'] found = True if not found: raise Exception("Failed to find container: %s" % c) if alpha_init_containers: annotation = 'pod.alpha.kubernetes.io/init-containers' v = alpha_init_containers.values() pod['metadata']['annotations'][annotation] = json.dumps(v) multidoc['items'].append(res) if skip_and_return: if multi: return yaml.safe_dump(multidoc) else: return yaml.safe_dump(res) if args.output == 'json': if multi: print(json.dumps(multidoc, indent=4), end="") else: print(json.dumps(res, indent=4), end="") elif multi: print(yaml.safe_dump(multidoc)) else: if args.debug_container is not None: print(yaml.safe_dump(res), end="") else: print(raw_doc, end="")
def doDescribeAndCheck(self): # This yaml segment may be empty (comments-only) if self.y is None: self.oks.append('Yaml segment is empty of content and perhaps ' 'only contains comments') return # Allow to succeed # Create the command to send this single resource yaml blob to # kubectl to query its existence. cmd = ('echo \'{}\' | kubectl describe -f -'.format( self.definition.replace("'", "'\\''"))) # escape for bash out, err = ExecUtils.exec_command(cmd) # Check if kubectl returns non-zero exit status if err is not None: self.errors.append('Either resource does not exist, ' 'or invalid resource yaml') return # For all resource types, check the Name to verify existence name = KubeResourceYamlStatus._matchSingleLineField('Name', out) if name is None or name != self.getName(): self.errors.append('No resource with name {} exists'.format( self.getName())) else: self.oks.append('Verified resource with name {} exists'.format( self.getName())) # For PersistentVolumes and PersistentVolumeClaims if self.getKind() == 'PersistentVolume' or ( self.getKind() == 'PersistentVolumeClaim'): # Verify that the PV/PVC is bound status = KubeResourceYamlStatus._matchSingleLineField( 'Status', out) if status is None or status != "Bound": self.errors.append("{} not Bound".format(self.getKind())) else: self.oks.append("{} Bound".format(self.getKind())) # For Services if self.getKind() == 'Service': # Verify the service has an IP ip = KubeResourceYamlStatus._matchSingleLineField('IP', out) if ip is None or len(ip) == 0: self.errors.append("{} has no IP".format(self.getKind())) else: self.oks.append("{} has IP".format(self.getKind())) # For ReplicationControllers # Replicas: 1 current / 1 desired # Pods Status: 1 Running / 0 Waiting / 0 Succeeded / 0 Failed if self.getKind() == 'ReplicationController': # Verify the rc has the right number of replicas replicas = KubeResourceYamlStatus._matchSingleLineField( 'Replicas', out) if replicas is None: self.errors.append("{} replicas not found".format( self.getKind())) else: self.oks.append("{} replicas found".format(self.getKind())) replicas_detail = KubeResourceYamlStatus._matchReturnGroups( '^(\d+) current / (\d+) desired', replicas) if replicas_detail is not None: current, desired = replicas_detail if current != desired: self.errors.append( "current != desired: {}".format(replicas)) else: self.oks.append( "current == desired: {}".format(replicas)) # Verify the rc has the right number of pod_status pod_status = KubeResourceYamlStatus._matchSingleLineField( 'Pods Status', out) if pod_status is None: self.errors.append("{} pod_status not found".format( self.getKind())) else: self.oks.append("{} pod_status found".format(self.getKind())) pod_status_detail = KubeResourceYamlStatus._matchReturnGroups( '^(\d+) Running / (\d+) Waiting /' ' (\d+) Succeeded / (\d+) Failed', pod_status) if pod_status_detail is not None: running, waiting, succeeded, failed = pod_status_detail if (int(running) == 0 or int(waiting) > 0 or (int(failed) > 0)): self.errors.append( "pod_status has errors {}".format(pod_status)) else: self.oks.append( "pod_status has no errors: {}".format(pod_status))