def create_smlite_yamls(yaml_instance, frontend, hostname): global ports_used global cpx_count global cpx_ingress_class global chart_name global frontend_cpx_count headless_svc_tag = "-headless" ew_ing_tag = "-ingress" ns_ing_tag = "-ns-ingress" request_other_namespace = ".svc.cluster.local" restricted_ports = [9080, 9443, 3010, 5555, 8080] protocols_supported = ['tcp', 'udp', 'http', 'https', 'grpc'] list_of_sml_yamls = [] if yaml_instance["kind"] == "Service": service_name = yaml_instance["metadata"]["name"] prompts = chain( [ "Please enter protocol to be used for service \"" + service_name + "\" (tcp/udp/http/https/grpc): " ], repeat( "Invaild protocol. Please choose a protocol from (tcp/udp/http/https/grpc): " )) protocol = validate_input(prompts, "protocol") ports = yaml_instance["spec"]["ports"] if len(ports) > 1: port_list = [] for p in ports: port_list.append(p["port"]) prompts = chain( [ "Found multiple ports in the service \"" + service_name + "\". Please enter port to be used " + str(port_list)[1:-1] + ": " ], repeat( "Invaild port. Please provide a valid port to be used from " + str(port_list)[1:-1] + ": ")) service_port = validate_input(prompts, "port", port_list) port_details = ports[port_list.index(service_port)] else: port_details = yaml_instance["spec"]["ports"][0] if port_details["port"] in restricted_ports: logger.error("Microservice " + service_name + " using CPX restricted port " + str(port_details["port"]) + ". So, can't be converted into SML YAMLs") sys.exit(0) if (protocol.lower() == "tcp" or protocol.lower() == "udp" or protocol.lower() == "grpc") and port_details["port"] in ports_used: cpx_count = cpx_count + 1 if protocol == "grpc": protocol = "tcp" if protocol == "https": tls = input("Please give secret-name for TLS certificate: ") else: tls = "" ''' Creating Headless Service pointing to appication deployment ''' yaml_instance["metadata"]["name"] = service_name + headless_svc_tag yaml_instance["spec"]["clusterIP"] = "None" list_of_sml_yamls.append(yaml_instance) ''' Creating service for appication so that FQDN of the microservice points to Citrix ADC CPX IP address ''' service_yaml = manifestCreator.service({ "name": service_name, "appLabel": chart_name + str(cpx_count) + "-cpx", "ports": ports }) service_yaml = service_yaml.create() list_of_sml_yamls.append(service_yaml) ''' Creating ingress for each microservice so they get configured in CPX ''' if (protocol.lower() == "tcp" or protocol.lower() == "udp"): # if "targetPort" in port_details: # ingress_yaml = manifestCreator.ingress({"name":service_name+ew_ing_tag,"protocol":protocol.lower(), # "ingressClass":cpx_ingress_class+str(cpx_count),"tls":tls, # "serviceDetails":{"serviceName":service_name+headless_svc_tag, # "servicePort": port_details["targetPort"]}}) # else: ingress_yaml = manifestCreator.ingress({ "name": service_name + ew_ing_tag, "protocol": protocol.lower(), "ingressClass": cpx_ingress_class + str(cpx_count), "tls": tls, "serviceDetails": { "serviceName": service_name + headless_svc_tag, "servicePort": port_details["port"] } }) if frontend == service_name: frontend_ingress_yaml = manifestCreator.ingress({ "name": service_name + ns_ing_tag, "protocol": protocol.lower(), "ingressClass": cpx_ingress_class + str(cpx_count), "tls": tls, "serviceDetails": { "serviceName": service_name + headless_svc_tag, "servicePort": port_details["port"] } }) else: # if "targetPort" in port_details: # ingress_yaml = manifestCreator.ingress({"name":service_name+ew_ing_tag,"protocol":protocol.lower(), # "ingressClass":cpx_ingress_class+str(cpx_count),"tls":tls, # "serviceDetails":{service_name:[{"serviceName":service_name+headless_svc_tag, # "servicePort":port_details["targetPort"]}]}}) # else: ingress_yaml = manifestCreator.ingress({ "name": service_name + ew_ing_tag, "protocol": protocol.lower(), "ingressClass": cpx_ingress_class + str(cpx_count), "tls": tls, "serviceDetails": { service_name: [{ "serviceName": service_name + headless_svc_tag, "servicePort": port_details["port"] }] } }) # service_name+"."+namespace+request_other_namespace:[{ # "serviceName":service_name+headless_svc_tag, # "servicePort":port_details["port"]}]}}) if frontend == service_name: frontend_cpx_count = str(cpx_count) frontend_ingress_yaml = manifestCreator.ingress({ "name": service_name + ns_ing_tag, "protocol": protocol.lower(), "ingressClass": cpx_ingress_class + str(cpx_count), "tls": tls, "serviceDetails": { hostname: [{ "serviceName": service_name + headless_svc_tag, "servicePort": port_details["port"] }] } }) ingress_yaml = ingress_yaml.create() list_of_sml_yamls.append(ingress_yaml) if frontend == service_name: ingress_yaml = frontend_ingress_yaml.create() list_of_sml_yamls.append(ingress_yaml) if (protocol.lower() == "tcp" or protocol.lower() == "udp"): ports_used.append(port_details["port"]) ''' Keeping Deployment YAML as it is. ''' if yaml_instance["kind"] == "Deployment": list_of_sml_yamls.append(yaml_instance) return list_of_sml_yamls
def main(): global cpx_count global output_yaml parser = argparse.ArgumentParser( description= 'Taking service yamls or name as input. And namespace in case service names is provided' ) parser.add_argument( 'ServiceList', action="store", help= 'Please provide comma seperated list of service yamls or names to convert into SML' ) parser.add_argument( 'Namespace', action="store", nargs='?', help='Please provide namespace in which services are deployed') svcs = parser.parse_args().ServiceList.split(',') namespace = parser.parse_args().Namespace converted_yamls_dict_list = [] if (".yaml" not in svcs[0]): ''' Code for connecting to Kubenetes Cluster ''' if not namespace: logger.error( "Please provide namespace as well in which services are deployed while starting the script" ) sys.exit(0) prompts = chain( ["Do you want to connect to a Remote Kubernetes Cluster? (Y/N): "], repeat("Invaild input. Please respond with (Y/N): ")) remote_access = validate_input(prompts, "yesORno") if remote_access.lower() == 'n': prompts = chain([ 'Do you want to use default kubeconfig file present at "/root/.kube/config"? (Y/N): ' ], repeat("Invaild input. Please respond with (Y/N): ")) default_kube_config = validate_input(prompts, "yesORno") if default_kube_config.lower() == "y": kube_config_path = "/root/.kube/config" else: prompts = chain( ["Please provide path of kubeconfig file: "], repeat( "This path doesn't exist! Please provide a valid kubeconfig file path: " )) kube_config_path = validate_input(prompts, "path") config.load_kube_config(config_file=kube_config_path) v1 = client.CoreV1Api() elif remote_access.lower() == 'y': prompts = chain([ "Do you want to use Bearer Token for connecting to a Remote Kubernetes Cluster? (Y/N): " ], repeat("Invaild input. Please respond with (Y/N): ")) use_bearer_token = validate_input(prompts, "yesORno") if use_bearer_token.lower() == "y": configuration = client.Configuration() bearer_token = input( "Please provide bearer token key of SA having permission to access given service: " ) configuration.api_key = { "authorization": "Bearer " + bearer_token } prompts = chain( ["Please provide API server <IP:PORT>: "], repeat( "Invaild input. Please provide valid IP and port in format <IP:PORT>: " )) api_server = validate_input(prompts, "apiServerIPPort") configuration.host = 'https://' + api_server configuration.verify_ssl = False v1 = client.CoreV1Api(client.ApiClient(configuration)) elif use_bearer_token.lower() == "n": prompts = chain( ["Please provide path of kubeconfig file: "], repeat( "This path doesn't exist! Please provide a valid kubeconfig file path: " )) kube_config_path = validate_input(prompts, "path") config.load_kube_config(config_file=kube_config_path) v1 = client.CoreV1Api() app_frontend_svc_name = input( "Please provide name of the service exposed to tier-1: ") app_hostname = input("Please provide hostname for exposing \"" + app_frontend_svc_name + "\" service: ") if not namespace: namespace = "default" ''' Coverting YAMLs to SML YAMLs ''' for svc in svcs: if (".yaml" in svc): ''' When applcation YAMLs is provided as input. ''' with open(svc, 'r') as stream: try: yaml_dictionaries = yaml.safe_load_all(stream) for yaml_instance in yaml_dictionaries: if yaml_instance: converted_yamls_dict_list.extend( convert_yaml_to_sml(yaml_instance, app_frontend_svc_name, app_hostname)) except Exception as e: logger.error( "Retrieving yaml from service yamls provided failed with error: {}" .format(e)) sys.exit("Please ensure service yaml is in proper format") else: ''' When service name running in Kubernetes cluster is provided as input. ''' try: service = v1.read_namespaced_service(svc, namespace) except Exception as e: logger.error( "Retrieving service from KubeAPI server failed with error: {}" .format(e)) sys.exit("Please ensure service name and namespace is correct") service = service.__dict__ service = convert_kube_object_to_dict(service) # service = OrderedDict([('apiVersion',service['apiVersion']), # ('kind',service['kind']), # ('metadata',service['metadata']), # ('spec',service['spec']), converted_yamls_dict_list.extend( convert_yaml_to_sml(service, app_frontend_svc_name, app_hostname)) ''' Checking if required HELM is there or not. ''' helm_ver = subprocess.run(["helm version"], shell=True, capture_output=True).stdout.decode('utf-8') matchObj = re.search(r'Version:"v3.*', helm_ver, re.M | re.I) if not matchObj: logger.error("Couldn't find Helm with version 3.x and above.") sys.exit( "Please install Helm 3.x and put it in the PATH. This https://github.com/citrix/citrix-helm-charts/blob/master/Helm_Installation_version_3.md can be followed for the same." ) subprocess.run( ["helm repo add citrix https://citrix.github.io/citrix-helm-charts/"], shell=True, capture_output=True) ''' Getting ADM details ''' prompts = chain(['Citrix ADM required? (Y/N): '], repeat("Invaild input. Please respond with (Y/N): ")) adm_required = validate_input(prompts, "yesORno") if adm_required.lower() == "y": prompts = chain([ 'Please provide IP of ADM Agent(svcIP of container agent) for Citrix ADC CPX: ' ], repeat("Invaild IP. Please enter and IPv4 IP: ")) cpx_adm_agent_ip = validate_input(prompts, "ip") adm_secret = input( "Please provide name of K8s Secret created using ADM Agent credentials. Press ENTER for 'admlogin': "******"admlogin" ''' Adding CPX yamls ''' for i in range(1, cpx_count + 1): if adm_required.lower() == "y": output = subprocess.run([ "helm template " + chart_name + str(i) + " citrix/citrix-cloud-native --set cpx.enabled=true,cpx.license.accept=yes,cpx.ingressClass[0]=" + cpx_ingress_class + str(i) + ",cpx.coeConfig.required=true,cpx.coeConfig.timeseries.metrics.enable=true,cpx.coeConfig.distributedTracing.enable=true,cpx.coeConfig.endpoint.server=" + cpx_adm_agent_ip + ",cpx.ADMSettings.ADMIP=" + cpx_adm_agent_ip + ",cpx.ADMSettings.loginSecret=" + adm_secret + " -n " + namespace + " > temp.yaml" ], shell=True, capture_output=True) # output = subprocess.run(["helm template "+ chart_name + str(i) + " citrix/citrix-cloud-native --set cpx.enabled=true,cpx.license.accept=yes,cpx.ingressClass[0]="+cpx_ingress_class+str(i)+",cpx.coeConfig.required=true,cpx.coeConfig.timeseries.metrics.enable=true,cpx.coeConfig.distributedTracing.enable=true,cpx.coeConfig.endpoint.server="+ cpx_adm_agent_ip +",cpx.ADMSettings.ADMIP=" + cpx_adm_agent_ip +",cpx.ADMSettings.loginSecret=" + adm_secret + " -n "+namespace+" > temp.yaml"],shell=True,capture_output=True) if output.stderr: logger.error("Helm chart command failed with error {}".format( output.stderr)) sys.exit("Please ensure you have provided proper inputs.") else: output = subprocess.run([ "helm template " + chart_name + str(i) + " citrix/citrix-cloud-native --set cpx.enabled=true,cpx.license.accept=yes,cpx.ingressClass[0]=" + cpx_ingress_class + str(i) + " -n " + namespace + " > temp.yaml" ], shell=True, capture_output=True) if output.stderr: logger.error("Helm chart command failed with error {}".format( output.stderr)) sys.exit("Please ensure you have provided proper inputs.") with open('temp.yaml', 'r') as stream: yaml_dictionaries = list(yaml.safe_load_all(stream)) if i > 1: list_to_remove = [] for yaml_instance in yaml_dictionaries: if yaml_instance['kind'] == "ServiceAccount" or yaml_instance[ 'kind'] == "ClusterRole" or yaml_instance[ 'kind'] == "ClusterRoleBinding": list_to_remove.append(yaml_instance) elif yaml_instance['kind'] == "Deployment": yaml_instance['spec']['template']['spec'][ 'serviceAccountName'] = re.sub( "sml" + str(i), "sml1", yaml_instance['spec'] ['template']['spec']['serviceAccountName']) elif yaml_instance['kind'] == "Service": yaml_instance['metadata']['labels']['citrix-adc'] = 'cpx' yaml_dictionaries = [ x for x in yaml_dictionaries if x not in list_to_remove ] converted_yamls_dict_list.extend(yaml_dictionaries) else: for yaml_instance in yaml_dictionaries: if yaml_instance['kind'] == "Service": yaml_instance['metadata']['labels']['citrix-adc'] = 'cpx' converted_yamls_dict_list.extend(yaml_dictionaries) # rbac = manifestCreator.rbac() # rbac = rbac.createRbac() # converted_yamls_dict_list.extend(rbac) # cpx_cic_yaml = manifestCreator.cpxCic({"name":chart_name+str(i),"ingressClass":cpx_ingress_class+str(i)}) # cpx_cic_yaml = cpx_cic_yaml.create() # converted_yamls_dict_list.extend(cpx_cic_yaml) ''' Getting details for Tier-1 ADC ''' prompts = chain( ['Citrix Ingress Controller for tier-1 ADC required? (Y/N): '], repeat("Invaild input. Please respond with (Y/N): ")) create_cic = validate_input(prompts, "yesORno") if create_cic.lower() == "y": prompts = chain(['Please provide tier-1 ADC NSIP: '], repeat("Invaild IP. Please enter and IPv4 IP: ")) ns_ip = validate_input(prompts, "ip") prompts = chain(['Please provide tier-1 ADC VIP: '], repeat("Invaild IP. Please enter and IPv4 IP: ")) ns_vip = validate_input(prompts, "ip") ns_secret = input( "Please provide name of K8s Secret created using ADC credentials. Press ENTER for 'nslogin': "******"nslogin" if adm_required.lower() == "y": prompts = chain([ 'Please provide IP of ADM Agent(podIP of container agent) for Citrix ADC VPX/MPX: ' ], repeat("Invaild IP. Please enter and IPv4 IP: ")) vpx_adm_agent_ip = validate_input(prompts, "ip") app_frontend_svc_port = input( "Please provide port used to expose CPX service to Tier-1 ADC: ") prompts = chain( [ "Please provide protocol used to expose CPX service to Tier-1 ADC (tcp/udp/http/https/grpc): " ], repeat( "Invaild protocol. Please choose a protocol from (tcp/udp/http/https/grpc): " )) app_frontend_svc_protocol = validate_input(prompts, "protocol") if app_frontend_svc_protocol == "grpc": app_frontend_svc_protocol = "tcp" if app_frontend_svc_protocol == "https": tls = input("Please give secret-name for TLS certificate for \"" + app_frontend_svc_name + "\" svc: ") else: tls = "" if adm_required.lower() == "y": output = subprocess.run([ "helm template " + chart_name + " citrix/citrix-cloud-native --set cic.enabled=true,cic.nsIP=" + ns_ip + ",cic.nsVIP=" + ns_vip + ",cic.license.accept=yes,cic.adcCredentialSecret=" + ns_secret + ",cic.ingressClass[0]=" + vpx_ingress_class + ",cic.coeConfig.required=true,cic.coeConfig.timeseries.metrics.enable=true,cic.coeConfig.timeseries.port=5563,cic.coeConfig.distributedTracing.enable=true,cic.coeConfig.transactions.enable=true,cic.coeConfig.transactions.port=5557,cic.coeConfig.endpoint.server=" + vpx_adm_agent_ip + " -n " + namespace + " > temp.yaml" ], shell=True, capture_output=True) if output.stderr: logger.error("Helm chart command failed with error {}".format( output.stderr)) sys.exit("Please ensure you have provided proper inputs.") ingress_yaml = manifestCreator.ingress({ "name": tier1_ingress_name, "protocol": app_frontend_svc_protocol.lower(), "ingressClass": vpx_ingress_class, "tls": tls, "admRequired": True, "serviceDetails": { app_hostname: [{ "serviceName": "sml" + str(frontend_cpx_count) + "-cpx-service", "servicePort": app_frontend_svc_port }] } }) else: output = subprocess.run([ "helm template " + chart_name + " citrix/citrix-cloud-native --set cic.enabled=true,cic.nsIP=" + ns_ip + ",cic.nsVIP=" + ns_vip + ",cic.license.accept=yes,cic.adcCredentialSecret=" + ns_secret + ",cic.ingressClass[0]=" + vpx_ingress_class + " -n " + namespace + " > temp.yaml" ], shell=True, capture_output=True) if output.stderr: logger.error("Helm chart command failed with error {}".format( output.stderr)) sys.exit("Please ensure you have provided proper inputs.") ingress_yaml = manifestCreator.ingress({ "name": tier1_ingress_name, "protocol": app_frontend_svc_protocol.lower(), "ingressClass": vpx_ingress_class, "tls": tls, "serviceDetails": { app_hostname: [{ "serviceName": "sml" + str(frontend_cpx_count) + "-cpx-service", "servicePort": app_frontend_svc_port }] } }) with open('temp.yaml', 'r') as stream: yaml_dictionaries = list(yaml.safe_load_all(stream)) converted_yamls_dict_list.extend(yaml_dictionaries) ingress_yaml = ingress_yaml.create() converted_yamls_dict_list.append(ingress_yaml) ''' Deleting temp.yaml which got created via Helm ''' my_file = Path("temp.yaml") if my_file.is_file(): os.remove("temp.yaml") ''' Writing dictionay of YAML into a .yaml file ''' write_dictionaries_into_yaml(converted_yamls_dict_list, output_yaml)