def start_charm(): layer.status.maintenance('configuring container') image_info = layer.docker_resource.get_info('oci-image') service_name = hookenv.service_name() crd = yaml.load(Path('files/crd-v1beta1.yaml').read_text()) port = hookenv.config('port') layer.caas_base.pod_spec_set({ 'service': { 'annotations': { 'getambassador.io/config': yaml.dump_all([{ 'apiVersion': 'ambassador/v0', 'kind': 'Mapping', 'name': 'pipelines-viewer', 'prefix': '/data', 'rewrite': '/data', 'service': f'{service_name}:{port}', 'use_websocket': True, 'timeout_ms': 30000, }]) } }, 'containers': [{ 'name': 'pipelines-viewer', 'imageDetails': { 'imagePath': image_info.registry_path, 'username': image_info.username, 'password': image_info.password, }, 'ports': [{ 'name': 'ui', 'containerPort': port }], }], 'customResourceDefinitions': { crd['metadata']['name']: crd['spec'] }, }) layer.status.maintenance('creating container') set_flag('charm.started')
def start_charm(): layer.status.maintenance('configuring container') image_info = layer.docker_resource.get_info('oci-image') service_name = hookenv.service_name() port = hookenv.config('port') layer.caas_base.pod_spec_set( { 'service': { 'annotations': { 'getambassador.io/config': yaml.dump_all( [ { 'apiVersion': 'ambassador/v0', 'kind': 'Mapping', 'name': 'dashboard-mapping', 'prefix': '/', 'rewrite': '/', 'service': f'{service_name}:{port}', 'timeout_ms': 30000, } ] ) } }, 'containers': [ { 'name': 'pipelines-dashboard', 'imageDetails': { 'imagePath': image_info.registry_path, 'username': image_info.username, 'password': image_info.password, }, 'ports': [{'name': 'ui', 'containerPort': port}], } ], } ) layer.status.maintenance('creating container') set_flag('charm.started')
def start_charm(): layer.status.maintenance("configuring container") image_info = layer.docker_resource.get_info("oci-image") namespace = os.environ["JUJU_MODEL_NAME"] try: pilot = endpoint_from_name("istio-pilot").services()[0] except IndexError: hookenv.log("Got istio-pilot endpoint without any services") layer.status.maintenance("Waiting for istio-pilot relation") return False pilot_service = pilot['hosts'][0]['hostname'] pilot_port = pilot['hosts'][0]['port'] cfg = dict(hookenv.config()) # Talk to the K8s API to read the auto-generated root certificate secret. # Borrow the env vars from the root process that let the Kubernetes # client automatically look up connection info, since `load_incluster_config` # for whatever reason doesn't support loading the serviceaccount token from disk. os.environ.update( dict( e.split("=") for e in Path("/proc/1/environ").read_text().split("\x00") if "KUBERNETES_SERVICE" in e)) config.load_incluster_config() v1 = client.CoreV1Api() layer.status.maintenance( "Waiting for configmap/istio-ca-root-cert to be created") try: config_map = v1.read_namespaced_config_map(name="istio-ca-root-cert", namespace=namespace) if not config_map.data.get("root-cert.pem"): hookenv.log("Got empty certificate, waiting for real one") return False except client.rest.ApiException as err: hookenv.log(err) hookenv.log(err.status) hookenv.log(err.reason) hookenv.log(err.body) hookenv.log(err.headers) layer.status.blocked("istio-ca-root-cert certificate not found.") return False layer.caas_base.pod_spec_set({ "version": 3, "serviceAccount": { "roles": [{ "global": True, "rules": [ { "apiGroups": ["*"], "resources": ["*"], "verbs": ["*"] }, { "nonResourceURLs": ["*"], "verbs": ["*"] }, ], }] }, "containers": [{ "name": "istio-proxy", "args": [ "proxy", "router", "--domain", f"{namespace}.svc.cluster.local", "--proxyLogLevel=warning", "--proxyComponentLogLevel=misc:error", f"--log_output_level={cfg['log-level']}", "--drainDuration", "45s", "--parentShutdownDuration", "1m0s", "--connectTimeout", "10s", "--serviceCluster", hookenv.service_name(), "--proxyAdminPort", cfg['proxy-admin-port'], "--statusPort", str(cfg['status-port']), "--controlPlaneAuthPolicy", "NONE", "--discoveryAddress", f"{pilot_service}:{pilot_port}", "--trust-domain=cluster.local", ], "imageDetails": { "imagePath": image_info.registry_path, "username": image_info.username, "password": image_info.password, }, "envConfig": { "JWT_POLICY": "first-party-jwt", "PILOT_CERT_PROVIDER": "istiod", "ISTIO_META_USER_SDS": "true", "CA_ADDR": f"{pilot_service}:{pilot_port}", "NODE_NAME": { "field": { "path": "spec.nodeName", "api-version": "v1" } }, "POD_NAME": { "field": { "path": "metadata.name", "api-version": "v1" } }, "POD_NAMESPACE": namespace, "INSTANCE_IP": { "field": { "path": "status.podIP", "api-version": "v1" } }, "HOST_IP": { "field": { "path": "status.hostIP", "api-version": "v1" } }, "SERVICE_ACCOUNT": { "field": { "path": "spec.serviceAccountName", "api-version": "v1" } }, "ISTIO_META_WORKLOAD_NAME": hookenv.service_name(), "ISTIO_META_OWNER": f"kubernetes://api/apps/v1/namespaces/{namespace}/deployments/{hookenv.service_name()}", "ISTIO_META_MESH_ID": "cluster.local", "ISTIO_AUTO_MTLS_ENABLED": "true", "ISTIO_META_POD_NAME": { "field": { "path": "metadata.name", "api-version": "v1" } }, "ISTIO_META_CONFIG_NAMESPACE": namespace, "ISTIO_META_ROUTER_MODE": "sni-dnat", "ISTIO_META_CLUSTER_ID": "Kubernetes", }, "ports": [ { "name": "status-port", "containerPort": cfg['status-port'] }, { "name": "http2", "containerPort": cfg['http-port'] }, { "name": "https", "containerPort": cfg['https-port'] }, { "name": "kiali", "containerPort": cfg['kiali-port'] }, { "name": "prometheus", "containerPort": cfg['prometheus-port'] }, { "name": "grafana", "containerPort": cfg['grafana-port'] }, { "name": "tracing", "containerPort": cfg['tracing-port'] }, { "name": "tls", "containerPort": cfg['tls-port'] }, { "name": "pilot", "containerPort": cfg['xds-ca-port-legacy'] }, { "name": "citadel", "containerPort": cfg['citadel-grpc-port'] }, { "name": "dns-tls", "containerPort": cfg['dns-tls-port'] }, ], "kubernetes": { "readinessProbe": { "failureThreshold": 30, "httpGet": { "path": "/healthz/ready", "port": cfg['status-port'], "scheme": "HTTP", }, "initialDelaySeconds": 1, "periodSeconds": 2, "successThreshold": 1, "timeoutSeconds": 1, }, }, "volumeConfig": [ { "name": "istiod-ca-cert", "mountPath": "/var/run/secrets/istio", "files": [{ "path": "root-cert.pem", "content": config_map.data["root-cert.pem"], }], }, { "name": "ingressgatewaysdsudspath", "mountPath": "/var/run/ingress_gateway", "emptyDir": { "medium": "Memory" }, }, { "name": "podinfo", "mountPath": "/etc/istio/pod", "files": [ { "path": "annotations", "content": 'sidecar.istio.io/inject="false"', }, { "path": "labels", "content": 'app="istio-ingressgateway"\nistio="ingressgateway"', }, ], }, ], }], }) layer.status.maintenance("creating container") set_flag("charm.started")
def start_charm(): layer.status.maintenance('configuring container') image_info = layer.docker_resource.get_info('oci-image') service_name = hookenv.service_name() crd = yaml.load(Path('files/crd-v1beta1.yaml').read_text()) port = hookenv.config('port') layer.caas_base.pod_spec_set( { 'version': 2, 'serviceAccount': { 'global': True, 'rules': [ { 'apiGroups': ['*'], 'resources': ['deployments', 'services'], 'verbs': [ 'create', 'get', 'list', 'watch', 'update', 'patch', 'delete' ], }, { 'apiGroups': ['kubeflow.org'], 'resources': ['viewers'], 'verbs': [ 'create', 'get', 'list', 'watch', 'update', 'patch', 'delete' ], }, ], }, 'service': { 'annotations': { 'getambassador.io/config': yaml.dump_all([{ 'apiVersion': 'ambassador/v0', 'kind': 'Mapping', 'name': 'pipelines-viewer', 'prefix': '/data', 'rewrite': '/data', 'service': f'{service_name}:{port}', 'use_websocket': True, 'timeout_ms': 30000, }]) } }, 'containers': [{ 'name': 'pipelines-viewer', 'imageDetails': { 'imagePath': image_info.registry_path, 'username': image_info.username, 'password': image_info.password, }, 'config': { 'POD_NAMESPACE': os.environ['JUJU_MODEL_NAME'] }, }], }, { 'kubernetesResources': { 'customResourceDefinitions': { crd['metadata']['name']: crd['spec'] } } }, ) layer.status.maintenance('creating container') set_flag('charm.started')
def configure_mesh(): endpoint_from_name('service-mesh').add_route( prefix='/', service=hookenv.service_name(), port=hookenv.config('port'))
def start_charm(): if not hookenv.is_leader(): hookenv.log("This unit is not a leader.") return False layer.status.maintenance('configuring container') image_info = layer.docker_resource.get_info('oci-image') service_name = hookenv.service_name() api = endpoint_from_name('pipelines-api').services()[0] minio = endpoint_from_name('minio').services()[0]['hosts'][0] port = hookenv.config('port') layer.caas_base.pod_spec_set({ 'version': 2, 'serviceAccount': { 'rules': [ { 'apiGroups': [''], 'resources': ['pods', 'pods/log'], 'verbs': ['create', 'get', 'list'], }, { 'apiGroups': ['kubeflow.org'], 'resources': ['viewers'], 'verbs': ['create', 'get', 'list', 'watch', 'delete'], }, ] }, 'service': { 'annotations': { 'getambassador.io/config': yaml.dump_all([{ 'apiVersion': 'ambassador/v0', 'kind': 'Mapping', 'name': 'pipeline-ui', 'prefix': '/pipeline', 'rewrite': '/pipeline', 'service': f'{service_name}:{port}', 'use_websocket': True, 'timeout_ms': 30000, }]) } }, 'containers': [{ 'name': 'pipelines-ui', 'imageDetails': { 'imagePath': image_info.registry_path, 'username': image_info.username, 'password': image_info.password, }, 'config': { 'ML_PIPELINE_SERVICE_HOST': api['service_name'], 'ML_PIPELINE_SERVICE_PORT': api['hosts'][0]['port'], 'MINIO_HOST': minio['hostname'], 'MINIO_PORT': minio['port'], 'MINIO_NAMESPACE': os.environ['JUJU_MODEL_NAME'], 'ALLOW_CUSTOM_VISUALIZATIONS': True, }, 'ports': [{ 'name': 'ui', 'containerPort': port }], }], }) layer.status.maintenance('creating container') set_flag('charm.started')
def start_charm(): layer.status.maintenance('configuring container') image_info = layer.docker_resource.get_info('oci-image') service_name = hookenv.service_name() model = os.environ['JUJU_MODEL_NAME'] profiles = endpoint_from_name('kubeflow-profiles').services()[0] profiles_service = profiles['service_name'] port = hookenv.config('port') profile = hookenv.config('profile') layer.caas_base.pod_spec_set( { 'version': 2, 'serviceAccount': { 'global': True, 'rules': [{ 'apiGroups': [''], 'resources': ['events', 'namespaces', 'nodes'], 'verbs': ['get', 'list', 'watch'], }], }, 'service': { 'annotations': { 'getambassador.io/config': yaml.dump_all([{ 'apiVersion': 'ambassador/v0', 'kind': 'Mapping', 'name': 'dashboard-mapping', 'prefix': '/', 'rewrite': '/', 'service': f'{service_name}:{port}', 'timeout_ms': 30000, }]) } }, 'containers': [{ 'name': 'kubeflow-dashboard', 'imageDetails': { 'imagePath': image_info.registry_path, 'username': image_info.username, 'password': image_info.password, }, 'config': { 'USERID_HEADER': 'kubeflow-userid', 'USERID_PREFIX': '', 'PROFILES_KFAM_SERVICE_HOST': f'{profiles_service}.{model}', }, 'ports': [{ 'name': 'ui', 'containerPort': port }], 'kubernetes': { 'livenessProbe': { 'httpGet': { 'path': '/healthz', 'port': 8082 }, 'initialDelaySeconds': 30, 'periodSeconds': 30, } }, }], }, { 'kubernetesResources': { 'customResourceDefinitions': { crd['metadata']['name']: crd['spec'] for crd in yaml.safe_load_all( Path("files/crds.yaml").read_text()) }, 'customResources': { 'profiles.kubeflow.org': [{ 'apiVersion': 'kubeflow.org/v1beta1', 'kind': 'Profile', 'metadata': { 'name': profile }, 'spec': { 'owner': { 'kind': 'User', 'name': profile } }, }] }, } }, ) layer.status.maintenance('creating container') set_flag('charm.started')
def start_charm(): layer.status.maintenance('configuring container') image_info = layer.docker_resource.get_info('oci-image') service_name = hookenv.service_name() mysql = endpoint_from_name('mysql') minio = endpoint_from_name('minio').services()[0]['hosts'][0] grpc_port = hookenv.config('grpc-port') http_port = hookenv.config('http-port') layer.caas_base.pod_spec_set({ 'service': { 'annotations': { 'getambassador.io/config': yaml.dump_all([{ 'apiVersion': 'ambassador/v0', 'kind': 'Mapping', 'name': 'pipeline-api', 'prefix': '/apis/v1beta1/pipelines', 'rewrite': '/apis/v1beta1/pipelines', 'service': f'{service_name}:{http_port}', 'use_websocket': True, 'timeout_ms': 30000, }]) } }, 'containers': [{ 'name': 'pipelines-api', 'imageDetails': { 'imagePath': image_info.registry_path, 'username': image_info.username, 'password': image_info.password, }, 'ports': [ { 'name': 'grpc', 'containerPort': grpc_port }, { 'name': 'http', 'containerPort': http_port }, ], 'config': { 'MYSQL_SERVICE_HOST': mysql.host(), 'MYSQL_SERVICE_PORT': mysql.port(), 'MINIO_SERVICE_SERVICE_HOST': minio['hostname'], 'MINIO_SERVICE_SERVICE_PORT': minio['port'], 'POD_NAMESPACE': os.environ['JUJU_MODEL_NAME'], }, 'files': [{ 'name': 'config', 'mountPath': '/config', 'files': { 'config.json': json.dumps({ 'DBConfig': { 'DriverName': 'mysql', 'DataSourceName': mysql.database(), 'User': mysql.user(), 'Password': mysql.password(), }, 'ObjectStoreConfig': { 'AccessKey': hookenv.config('minio-access-key'), 'SecretAccessKey': hookenv.config('minio-secret-key'), 'BucketName': hookenv.config('minio-bucket-name'), }, 'InitConnectionTimeout': '5s', }), 'sample_config.json': Path('files/sample_config.json').read_text(), }, }], }], }) layer.status.maintenance('creating container') set_flag('charm.started')
def start_charm(): layer.status.maintenance('configuring container') image_info = layer.docker_resource.get_info('oci-image') service_name = hookenv.service_name() minio = endpoint_from_name('minio').services()[0]['hosts'][0] mysql = endpoint_from_name('mysql') profiles = endpoint_from_name( 'kubeflow-profiles').services()[0]['hosts'][0] viz = endpoint_from_name( 'pipelines-visualization').services()[0]['hosts'][0] grpc_port = hookenv.config('grpc-port') http_port = hookenv.config('http-port') layer.caas_base.pod_spec_set( { 'version': 2, 'serviceAccount': { 'rules': [ { 'apiGroups': ['argoproj.io'], 'resources': ['workflows'], 'verbs': [ 'create', 'get', 'list', 'watch', 'update', 'patch', 'delete' ], }, { 'apiGroups': ['kubeflow.org'], 'resources': ['scheduledworkflows'], 'verbs': ['create', 'get', 'list', 'update', 'patch', 'delete'], }, { 'apiGroups': [''], 'resources': ['pods'], 'verbs': ['delete'] }, ] }, 'service': { 'annotations': { 'getambassador.io/config': yaml.dump_all([{ 'apiVersion': 'ambassador/v0', 'kind': 'Mapping', 'name': 'pipeline-api', 'prefix': '/apis/v1beta1/pipelines', 'rewrite': '/apis/v1beta1/pipelines', 'service': f'{service_name}:{http_port}', 'use_websocket': True, 'timeout_ms': 30000, }]) } }, 'containers': [{ 'name': 'pipelines-api', 'imageDetails': { 'imagePath': image_info.registry_path, 'username': image_info.username, 'password': image_info.password, }, 'ports': [ { 'name': 'grpc', 'containerPort': grpc_port }, { 'name': 'http', 'containerPort': http_port }, ], 'command': [ 'apiserver', '--config=/config', '--sampleconfig=/config/sample_config.json', '-logtostderr=true', ], 'config': { 'POD_NAMESPACE': os.environ['JUJU_MODEL_NAME'] }, 'files': [ { 'name': 'config', 'mountPath': '/config', 'files': { 'config.json': json.dumps({ 'DBConfig': { 'DriverName': 'mysql', 'DataSourceName': mysql.database(), 'Host': mysql.host(), 'Port': mysql.port(), 'User': '******', 'Password': mysql.root_password(), 'DBName': 'mlpipeline', }, 'ObjectStoreConfig': { 'Host': minio['hostname'], 'Port': minio['port'], 'AccessKey': hookenv.config('minio-access-key'), 'SecretAccessKey': hookenv.config('minio-secret-key'), 'BucketName': hookenv.config('minio-bucket-name'), }, 'InitConnectionTimeout': '5s', "DefaultPipelineRunnerServiceAccount": "pipeline-runner", "ML_PIPELINE_VISUALIZATIONSERVER_SERVICE_HOST": viz['hostname'], "ML_PIPELINE_VISUALIZATIONSERVER_SERVICE_PORT": viz['port'], 'PROFILES_KFAM_SERVICE_HOST': profiles['hostname'], 'PROFILES_KFAM_SERVICE_PORT': profiles['port'], }), 'sample_config.json': Path('files/sample_config.json').read_text(), }, }, { 'name': 'samples', 'mountPath': '/samples', 'files': { Path(sample).name: Path(sample).read_text() for sample in glob('files/*.yaml') }, }, ], }], }, k8s_resources={ 'kubernetesResources': { 'serviceAccounts': [{ 'name': 'pipeline-runner', 'rules': [ { 'apiGroups': [''], 'resources': ['secrets'], 'verbs': ['get'] }, { 'apiGroups': [''], 'resources': ['configmaps'], 'verbs': ['get', 'watch', 'list'], }, { 'apiGroups': [''], 'resources': ['persistentvolumeclaims'], 'verbs': ['create', 'delete', 'get'], }, { 'apiGroups': ['snapshot.storage.k8s.io'], 'resources': ['volumesnapshots'], 'verbs': ['create', 'delete', 'get'], }, { 'apiGroups': ['argoproj.io'], 'resources': ['workflows'], 'verbs': ['get', 'list', 'watch', 'update', 'patch'], }, { 'apiGroups': [''], 'resources': ['pods', 'pods/exec', 'pods/log', 'services'], 'verbs': ['*'], }, { 'apiGroups': ['', 'apps', 'extensions'], 'resources': ['deployments', 'replicasets'], 'verbs': ['*'], }, { 'apiGroups': ['kubeflow.org'], 'resources': ['*'], 'verbs': ['*'] }, { 'apiGroups': ['batch'], 'resources': ['jobs'], 'verbs': ['*'] }, ], }] } }, ) layer.status.maintenance('creating container') clear_flag('mysql.changed') set_flag('charm.started')
def start_charm(): if not hookenv.is_leader(): hookenv.log("This unit is not a leader.") return False layer.status.maintenance('configuring container') image_info = layer.docker_resource.get_info('oci-image') service_name = hookenv.service_name() api = endpoint_from_name('metadata-api').services()[0] grpc = endpoint_from_name('metadata-grpc').services()[0] port = hookenv.config('port') layer.caas_base.pod_spec_set({ 'version': 2, 'serviceAccount': { 'rules': [ { 'apiGroups': [''], 'resources': ['pods', 'pods/log'], 'verbs': ['create', 'get', 'list'], }, { 'apiGroups': ['kubeflow.org'], 'resources': ['viewers'], 'verbs': ['create', 'get', 'list', 'watch', 'delete'], }, ] }, 'service': { 'annotations': { 'getambassador.io/config': yaml.dump_all([{ 'apiVersion': 'ambassador/v0', 'kind': 'Mapping', 'name': 'metadata-ui', 'prefix': '/metadata', 'rewrite': '/metadata', 'service': f'{service_name}:{port}', 'use_websocket': True, 'timeout_ms': 30000, }]) } }, 'containers': [{ 'name': 'metadata-ui', 'imageDetails': { 'imagePath': image_info.registry_path, 'username': image_info.username, 'password': image_info.password, }, 'ports': [{ 'name': 'http', 'containerPort': port }], 'config': { 'METADATA_SERVICE_SERVICE_HOST': api['service_name'], 'METADATA_SERVICE_SERVICE_PORT': api['hosts'][0]['port'], 'METADATA_ENVOY_SERVICE_SERVICE_HOST': grpc['service_name'], 'METADATA_ENVOY_SERVICE_SERVICE_PORT': grpc['hosts'][0]['port'], }, }], }) layer.status.maintenance('creating container') set_flag('charm.started')
def configure_mesh(): endpoint_from_name('service-mesh').add_route( prefix='/apis/v1beta1/pipelines', service=hookenv.service_name(), port=hookenv.config('http-port'), )
def start_charm(): layer.status.maintenance('configuring container') image_info = layer.docker_resource.get_info('oci-image') service_name = hookenv.service_name() api = endpoint_from_name('pipelines-api').services()[0] ui_port = hookenv.config('ui-port') proxy_port = hookenv.config('proxy-port') layer.caas_base.pod_spec_set({ 'service': { 'annotations': { 'getambassador.io/config': yaml.dump_all([ { 'apiVersion': 'ambassador/v0', 'kind': 'Mapping', 'name': 'pipeline-ui', 'prefix': '/pipeline', 'rewrite': '/pipeline', 'service': f'{service_name}:{ui_port}', 'use_websocket': True, 'timeout_ms': 30000, }, { 'apiVersion': 'ambassador/v0', 'kind': 'Mapping', 'name': 'pipeline-ui-apis', 'prefix': '/apis', 'rewrite': '/apis', 'service': f'{service_name}:{proxy_port}', 'use_websocket': True, 'timeout_ms': 30000, }, ]) } }, 'containers': [{ 'name': 'pipelines-ui', 'imageDetails': { 'imagePath': image_info.registry_path, 'username': image_info.username, 'password': image_info.password, }, 'config': { 'ML_PIPELINE_SERVICE_HOST': api['service_name'], 'ML_PIPELINE_SERVICE_PORT': api['hosts'][0]['port'], }, 'ports': [ { 'name': 'ui', 'containerPort': ui_port }, { 'name': 'api-proxy', 'containerPort': proxy_port }, ], }], }) layer.status.maintenance('creating container') set_flag('charm.started')