added azure pipeline example

Co-Authored-By: Mohona Ahmed <t-moahme@microsoft.com>
Co-Authored-By: Maanav Dalal <maanavdalal@gmail.com>
Co-Authored-By: sethjuarez <me@sethjuarez.com>
This commit is contained in:
Rebecca McFadden 2019-07-17 15:04:04 -07:00
parent ac9f2f1238
commit 17ddeb49fb
513 changed files with 65067 additions and 0 deletions

View File

0
code_search/docker/ks/submit_code_embeddings_job.sh Executable file → Normal file
View File

0
code_search/docker/ks/update_index.sh Executable file → Normal file
View File

0
code_search/docker/t2t/t2t-entrypoint.sh Executable file → Normal file
View File

0
code_search/docker/ui/build.sh Executable file → Normal file
View File

View File

0
demos/simple_pipeline/gpu-example-pipeline.py Executable file → Normal file
View File

0
demos/yelp_demo/demo_setup/create_context.sh Executable file → Normal file
View File

0
demos/yelp_demo/pipelines/gpu-example-pipeline.py Executable file → Normal file
View File

0
demos/yelp_demo/yelp/yelp_sentiment/worker_launcher.sh Executable file → Normal file
View File

0
github_issue_summarization/Makefile Executable file → Normal file
View File

0
mnist/Makefile Executable file → Normal file
View File

0
mnist/training/base/definition.sh Executable file → Normal file
View File

18
pipelines/azurepipeline/.gitignore vendored Normal file
View File

@ -0,0 +1,18 @@
# standard things
.vscode
.ipynb_checkpoints/
__pycache__
# Environment Variables
*.env
*.cfg
*-creds.yaml
# models and data
data/
model/
*.tar.gz
*.h5
*.zip
aml_config/

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,5 @@
[![Build Status](https://dev.azure.com/daaronch/Kubeflow%20and%20MLOps/_apis/build/status/aronchick.kubeflow-and-mlops?branchName=master)](https://dev.azure.com/daaronch/Kubeflow%20and%20MLOps/_build/latest?definitionId=3&branchName=master)
# Kubeflow and Azure Pipelines
More to come!

View File

@ -0,0 +1 @@
-n tacoprofile -m tacosandburritos -i /scripts/inferenceconfig.json -d '{"image":"https://www.exploreveg.org/files/2015/05/sofritas-burrito.jpeg"}' -t 72f988bf-86f1-41af-91ab-2d7cd011db47 -r taco-rg -w taco-workspace -s 2991e3e4-4e8d-451f-aa17-640d89c63144 -p xP2a6Bp1vSOjKholxl5bLPd.=r@ZZJn[ -u 1308a130-d549-44e1-ba66-ce8c487d76e3

View File

@ -0,0 +1,87 @@
# Kubeflow Container Build Pipeline
trigger:
- master
pr: none
stages:
- stage: ContainerConfig
displayName: 'Configure and Register Containers'
jobs:
- job: Containers
pool:
name: default
steps:
- task: AzureCLI@1
inputs:
azureSubscription: 'Shared Data Platform - R+D (1308a130-d549-44e1-ba66-ce8c487d76e3)'
scriptLocation: 'inlineScript'
inlineScript: '
sudo az acr login -n kubeflowregistry &&
cd code &&
cd preprocess &&
sudo docker build -t kubeflowregistry.azurecr.io/kubeflow/preprocess:$BUILD_SOURCEVERSION . &&
sudo docker push kubeflowregistry.azurecr.io/kubeflow/preprocess:$BUILD_SOURCEVERSION '
displayName: 'Build & Push Preprocess Image'
- task: AzureCLI@1
inputs:
azureSubscription: 'Shared Data Platform - R+D (1308a130-d549-44e1-ba66-ce8c487d76e3)'
scriptLocation: 'inlineScript'
inlineScript: '
cd code &&
cd training &&
sudo docker build -t kubeflowregistry.azurecr.io/kubeflow/training:$BUILD_SOURCEVERSION . &&
sudo docker push kubeflowregistry.azurecr.io/kubeflow/training:$BUILD_SOURCEVERSION '
displayName: 'Build & Push Training Image'
- task: AzureCLI@1
inputs:
azureSubscription: 'Shared Data Platform - R+D (1308a130-d549-44e1-ba66-ce8c487d76e3)'
scriptLocation: 'inlineScript'
inlineScript: '
cd code &&
cd register &&
sudo docker build -t kubeflowregistry.azurecr.io/kubeflow/register:$BUILD_SOURCEVERSION . &&
sudo docker push kubeflowregistry.azurecr.io/kubeflow/register:$BUILD_SOURCEVERSION '
displayName: 'Build & Push Register Image'
# Moved KF step to build
- stage: KubeflowTrigger
dependsOn: ContainerConfig
displayName: 'Trigger Kubeflow Pipeline'
variables:
- group: kf-variables
jobs:
- job: Kubeflow
pool:
name: default
steps:
- task: AzureCLI@1
env:
KF_MAPPED_SERVICE_PRINCIPAL_PASSWORD: $(KF_SERVICE_PRINCIPAL_PASSWORD)
inputs:
azureSubscription: 'Shared Data Platform - R+D (1308a130-d549-44e1-ba66-ce8c487d76e3)'
scriptLocation: 'inlineScript'
inlineScript: |
az aks get-credentials -g kubeflow-mlops-rg -n kubeflow-mlops-cluster
kubectl port-forward --namespace kubeflow svc/ml-pipeline 8888:8888 &
kubepid=$!
sudo apt-get install python3-setuptools
pip3 install wheel
pip3 install kfp
touch script.py
echo "import kfp" >> script.py
echo "client = kfp.Client(host='localhost:8888')" >> script.py
echo "client.run_pipeline('$KF_EXPERIMENT_ID', 'Run ${BUILD_BUILDID}', params={'imagetag': '${BUILD_SOURCEVERSION}', 'tenant-id': '$KF_TENANT_ID', 'service-principal-id': '$KF_SERVICE_PRINCIPAL_ID', 'service-principal-password': '$KF_MAPPED_SERVICE_PRINCIPAL_PASSWORD', 'subscription-id': '$KF_SUBSCRIPTION_ID', 'resource-group': '$KF_RESOURCE_GROUP', 'workspace': '$KF_WORKSPACE', 'persistent-volume-name': '$KF_PERSISTENT_VOLUME_NAME', 'persistent-volume-path': '$KF_PERSISTENT_VOLUME_PATH', 'data-download': '$KF_DATA_DOWNLOAD', 'epochs': '$KF_EPOCHS', 'batch': '$KF_BATCH', 'learning-rate': '$KF_LEARNING_RATE', 'model-name': '$KF_MODEL_NAME'}, pipeline_id='$KF_PIPELINE_ID')" >> script.py
cat script.py
python3 script.py
kill $kubepid
displayName: 'Trigger Kubeflow Pipeline'

View File

@ -0,0 +1,9 @@
FROM mcr.microsoft.com/azure-cli
RUN az extension add -n azure-cli-ml
RUN pip install --upgrade pip
COPY deploymentconfig.json /scripts/deploymentconfig.json
COPY inferenceconfig.json /scripts/inferenceconfig.json
COPY deploy.sh /scripts/deploy.sh
COPY score.py /scripts/score.py
COPY environment.yml /scripts/environment.yml
CMD bash

View File

@ -0,0 +1,4 @@
{
"computeType": "aks",
"ComputeTarget": "aks-cluster"
}

View File

@ -0,0 +1,20 @@
# az ml model deploy -n tacosandburritos -m tacosandburritos:1 --ic inferenceconfig.json --dc deploymentconfig.json --resource-group taco-rg --workspace-name taco-workspace --overwrite -v
#!/bin/sh
while getopts "m:n:i:d:s:p:u:r:w:t:b:" option;
do
case "$option" in
m ) MODEL=${OPTARG};;
n ) MODEL_NAME=${OPTARG};;
i ) INFERENCE_CONFIG=${OPTARG};;
d ) DEPLOYMENTCONFIG=${OPTARG};;
s ) SERVICE_PRINCIPAL_ID=${OPTARG};;
p ) SERVICE_PRINCIPAL_PASSWORD=${OPTARG};;
u ) SUBSCRIPTION_ID=${OPTARG};;
r ) RESOURCE_GROUP=${OPTARG};;
w ) WORKSPACE=${OPTARG};;
t ) TENANT_ID=${OPTARG};;
b ) BASE_PATH=${OPTARG};;
esac
done
az login --service-principal --username ${SERVICE_PRINCIPAL_ID} --password ${SERVICE_PRINCIPAL_PASSWORD} -t $TENANT_ID
az ml model deploy -n $MODEL_NAME -m ${MODEL}:1 --ic $INFERENCE_CONFIG --pi ${BASE_PATH}/model/myprofileresult.json --dc $DEPLOYMENTCONFIG -w $WORKSPACE -g $RESOURCE_GROUP --overwrite -v

View File

@ -0,0 +1,8 @@
{
"containerResourceRequirements": {
"cpu": 2,
"memoryInGB": 4
},
"computeType": "ACI",
"enableAppInsights": "True"
}

View File

@ -0,0 +1,19 @@
# Conda environment specification. The dependencies defined in this file will
# be automatically provisioned for runs with userManagedDependencies=False.
# Details about the Conda environment file format:
# https://conda.io/docs/user-guide/tasks/manage-environments.html#create-env-file-manually
name: project_environment
dependencies:
# The python interpreter version.
# Currently Azure ML only supports 3.5.2 and later.
- python=3.6.2
- pip:
# Required packages for AzureML execution, history, and data preparation.
- azureml-defaults
- numpy
- tensorflow==2.0.0-alpha0
- Pillow
- requests

View File

@ -0,0 +1,4 @@
echo "test the deployment with a burrito image"
az ml service run -n fooddeployaci -d '{ "image": "https://www.exploreveg.org/files/2015/05/sofritas-burrito.jpeg" }' -w taco-workspace -g taco-rg
echo "test the deployment with a taco image"
az ml service run -n fooddeployaci -d '{ "image": "https://c1.staticflickr.com/5/4022/4401140214_f489c708f0_b.jpg" }' -w taco-workspace -g taco-rg

View File

@ -0,0 +1,10 @@
{
"entryScript": "/scripts/score.py",
"runtime": "python",
"condaFile": "/scripts/environment.yml",
"extraDockerfileSteps": null,
"sourceDirectory": null,
"enableGpu": false,
"baseImage": null,
"baseImageRegistry": null
}

View File

@ -0,0 +1,90 @@
import json
import time
import requests
import datetime
import numpy as np
from PIL import Image
from io import BytesIO
import tensorflow as tf
from azureml.core.model import Model
def init():
global model
try:
model_path = Model.get_model_path('tacosandburritos')
except:
model_path = '/model/latest.h5'
print('Attempting to load model')
model = tf.keras.models.load_model(model_path)
model.summary()
print('Done!')
print('Initialized model "{}" at {}'.format(model_path, datetime.datetime.now()))
def run(raw_data):
global model
prev_time = time.time()
post = json.loads(raw_data)
img_path = post['image']
current_time = time.time()
tensor = process_image(img_path, 160)
t = tf.reshape(tensor, [-1, 160, 160, 3])
o = model.predict(t, steps=1)#[0][0]
print(o)
o = o[0][0]
inference_time = datetime.timedelta(seconds=current_time - prev_time)
payload = {
'time': inference_time.total_seconds(),
'prediction': 'burrito' if o < 0.5 else 'tacos',
'scores': str(o)
}
print('Input ({}), Prediction ({})'.format(post['image'], payload))
return payload
def process_image(path, image_size):
# Extract image (from web or path)
if(path.startswith('http')):
response = requests.get(path)
img = np.array(Image.open(BytesIO(response.content)))
else:
img = np.array(Image.open(path))
img_tensor = tf.convert_to_tensor(img, dtype=tf.float32)
#tf.image.decode_jpeg(img_raw, channels=3)
img_final = tf.image.resize(img_tensor, [image_size, image_size]) / 255
return img_final
def info(msg, char = "#", width = 75):
print("")
print(char * width)
print(char + " %0*s" % ((-1*width)+5, msg) + char)
print(char * width)
if __name__ == "__main__":
images = {
'tacos': 'https://c1.staticflickr.com/5/4022/4401140214_f489c708f0_b.jpg',
'burrito': 'https://www.exploreveg.org/files/2015/05/sofritas-burrito.jpeg'
}
init()
for k, v in images.items():
print('{} => {}'.format(k, v))
info('Taco Test')
taco = json.dumps({ 'image': images['tacos'] })
print(taco)
run(taco)
info('Burrito Test')
burrito = json.dumps({ 'image': images['burrito'] })
print(burrito)
run(burrito)

View File

@ -0,0 +1,9 @@
FROM tensorflow/tensorflow:2.0.0a0-gpu-py3
RUN pip install azure-cli
RUN az extension add -n azure-cli-ml
RUN pip install --upgrade pip
COPY profile.sh /scripts/profile.sh
COPY inferenceconfig.json /scripts/inferenceconfig.json
COPY score.py /scripts/score.py
COPY environment.yml /scripts/environment.yml
ENTRYPOINT bash

View File

@ -0,0 +1,45 @@
apiVersion: kfdef.apps.kubeflow.org/v1alpha1
kind: KfDef
metadata:
creationTimestamp: null
name: kflow
namespace: kubeflow
spec:
appdir: /home/rebec/kubeflow-and-mlops/code/kflow
componentParams:
ambassador:
- name: ambassadorServiceType
value: NodePort
components:
- ambassador
- argo
- centraldashboard
- jupyter-web-app
- katib
- metacontroller
- notebook-controller
- pipeline
- pytorch-operator
- tensorboard
- tf-job-operator
packages:
- argo
- common
- examples
- gcp
- jupyter
- katib
- metacontroller
- modeldb
- mpi-job
- pipeline
- pytorch-job
- seldon
- tensorboard
- tf-serving
- tf-training
repo: /home/rebec/kubeflow-and-mlops/code/kflow/.cache/v0.5.1/kubeflow
useBasicAuth: false
useIstio: false
version: v0.5.1
status: {}

View File

@ -0,0 +1,4 @@
/lib
/.ksonnet/registries
/app.override.yaml
/.ks_environment

View File

@ -0,0 +1,76 @@
apiVersion: 0.3.0
environments:
default:
destination:
namespace: kubeflow
server: https://taco-cls-taco-rg-1308a1-e98d0802.hcp.eastus.azmk8s.io:443
k8sVersion: v1.14.0
path: default
kind: ksonnet.io/app
libraries:
kubeflow/argo:
name: argo
registry: kubeflow
version: ""
kubeflow/common:
name: common
registry: kubeflow
version: ""
kubeflow/examples:
name: examples
registry: kubeflow
version: ""
kubeflow/gcp:
name: gcp
registry: kubeflow
version: ""
kubeflow/jupyter:
name: jupyter
registry: kubeflow
version: ""
kubeflow/katib:
name: katib
registry: kubeflow
version: ""
kubeflow/metacontroller:
name: metacontroller
registry: kubeflow
version: ""
kubeflow/modeldb:
name: modeldb
registry: kubeflow
version: ""
kubeflow/mpi-job:
name: mpi-job
registry: kubeflow
version: ""
kubeflow/pipeline:
name: pipeline
registry: kubeflow
version: ""
kubeflow/pytorch-job:
name: pytorch-job
registry: kubeflow
version: ""
kubeflow/seldon:
name: seldon
registry: kubeflow
version: ""
kubeflow/tensorboard:
name: tensorboard
registry: kubeflow
version: ""
kubeflow/tf-serving:
name: tf-serving
registry: kubeflow
version: ""
kubeflow/tf-training:
name: tf-training
registry: kubeflow
version: ""
name: ks_app
registries:
kubeflow:
protocol: fs
uri: /home/rebec/kubeflow-and-mlops/code/kflow/.cache/v0.5.1/kubeflow
version: 0.0.1

View File

@ -0,0 +1,6 @@
local env = std.extVar("__ksonnet/environments");
local params = std.extVar("__ksonnet/params").components.ambassador;
local ambassador = import "kubeflow/common/ambassador.libsonnet";
local instance = ambassador.new(env, params);
instance.list(instance.all)

View File

@ -0,0 +1,6 @@
local env = std.extVar("__ksonnet/environments");
local params = std.extVar("__ksonnet/params").components.argo;
local argo = import "kubeflow/argo/argo.libsonnet";
local instance = argo.new(env, params);
instance.list(instance.all)

View File

@ -0,0 +1,6 @@
local env = std.extVar("__ksonnet/environments");
local params = std.extVar("__ksonnet/params").components.centraldashboard;
local centraldashboard = import "kubeflow/common/centraldashboard.libsonnet";
local instance = centraldashboard.new(env, params);
instance.list(instance.all)

View File

@ -0,0 +1,7 @@
local env = std.extVar("__ksonnet/environments");
local params = std.extVar("__ksonnet/params").components["jupyter-web-app"];
local jupyter_ui = import "kubeflow/jupyter/jupyter-web-app.libsonnet";
local instance = jupyter_ui.new(env, params);
instance.list(instance.all)

View File

@ -0,0 +1,16 @@
local env = std.extVar("__ksonnet/environments");
local params = std.extVar("__ksonnet/params").components.katib;
local k = import "k.libsonnet";
local studyjobcontroller = import "kubeflow/katib/studyjobcontroller.libsonnet";
local suggestion = import "kubeflow/katib/suggestion.libsonnet";
local vizier = import "kubeflow/katib/vizier.libsonnet";
local namespace = env.namespace;
std.prune(
k.core.v1.list.new(vizier.all(params, namespace))
+ k.core.v1.list.new(suggestion.all(params, namespace))
+ k.core.v1.list.new(studyjobcontroller.all(params, namespace))
)

View File

@ -0,0 +1,6 @@
local env = std.extVar("__ksonnet/environments");
local params = std.extVar("__ksonnet/params").components.metacontroller;
local metacontroller = import "kubeflow/metacontroller/metacontroller.libsonnet";
local instance = metacontroller.new(env, params);
instance.list(instance.all)

View File

@ -0,0 +1,6 @@
local env = std.extVar("__ksonnet/environments");
local params = std.extVar("__ksonnet/params").components["notebook-controller"];
local notebooks = import "kubeflow/jupyter/notebook_controller.libsonnet";
local instance = notebooks.new(env, params);
instance.list(instance.all)

View File

@ -0,0 +1,93 @@
{
global: {},
components: {
// Component-level parameters, defined initially from 'ks prototype use ...'
// Each object below should correspond to a component in the components/ directory
ambassador: {
ambassadorImage: 'quay.io/datawire/ambassador:0.37.0',
ambassadorNodePort: 0,
ambassadorServiceType: 'NodePort',
name: 'ambassador',
platform: 'none',
replicas: 3,
},
argo: {
artifactRepositoryAccessKeySecretKey: 'accesskey',
artifactRepositoryAccessKeySecretName: 'mlpipeline-minio-artifact',
artifactRepositoryBucket: 'mlpipeline',
artifactRepositoryEndpoint: 'minio-service.kubeflow:9000',
artifactRepositoryInsecure: 'true',
artifactRepositoryKeyPrefix: 'artifacts',
artifactRepositorySecretKeySecretKey: 'secretkey',
artifactRepositorySecretKeySecretName: 'mlpipeline-minio-artifact',
executorImage: 'argoproj/argoexec:v2.2.0',
name: 'argo',
uiImage: 'argoproj/argoui:v2.2.0',
workflowControllerImage: 'argoproj/workflow-controller:v2.2.0',
},
centraldashboard: {
image: 'gcr.io/kubeflow-images-public/centraldashboard:v0.5.0',
name: 'centraldashboard',
},
"jupyter-web-app": {
image: 'gcr.io/kubeflow-images-public/jupyter-web-app:v0.5.0',
name: 'jupyter-web-app',
policy: 'Always',
port: '80',
prefix: 'jupyter',
rokSecretName: 'secret-rok-{username}',
ui: 'default',
},
katib: {
katibUIImage: 'gcr.io/kubeflow-images-public/katib/katib-ui:v0.1.2-alpha-156-g4ab3dbd',
metricsCollectorImage: 'gcr.io/kubeflow-images-public/katib/metrics-collector:v0.1.2-alpha-156-g4ab3dbd',
name: 'katib',
studyJobControllerImage: 'gcr.io/kubeflow-images-public/katib/studyjob-controller:v0.1.2-alpha-156-g4ab3dbd',
suggestionBayesianOptimizationImage: 'gcr.io/kubeflow-images-public/katib/suggestion-bayesianoptimization:v0.1.2-alpha-156-g4ab3dbd',
suggestionGridImage: 'gcr.io/kubeflow-images-public/katib/suggestion-grid:v0.1.2-alpha-156-g4ab3dbd',
suggestionHyperbandImage: 'gcr.io/kubeflow-images-public/katib/suggestion-hyperband:v0.1.2-alpha-156-g4ab3dbd',
suggestionRandomImage: 'gcr.io/kubeflow-images-public/katib/suggestion-random:v0.1.2-alpha-156-g4ab3dbd',
vizierCoreImage: 'gcr.io/kubeflow-images-public/katib/vizier-core:v0.1.2-alpha-156-g4ab3dbd',
vizierCoreRestImage: 'gcr.io/kubeflow-images-public/katib/vizier-core-rest:v0.1.2-alpha-156-g4ab3dbd',
vizierDbImage: 'mysql:8.0.3',
},
metacontroller: {
image: 'metacontroller/metacontroller:v0.3.0',
name: 'metacontroller',
},
"notebook-controller": {
controllerImage: 'gcr.io/kubeflow-images-public/notebook-controller:v20190401-v0.4.0-rc.1-308-g33618cc9-e3b0c4',
injectGcpCredentials: 'true',
name: 'notebook-controller',
},
pipeline: {
name: 'pipeline',
},
"pytorch-operator": {
cloud: 'null',
deploymentNamespace: 'null',
deploymentScope: 'cluster',
disks: 'null',
name: 'pytorch-operator',
pytorchDefaultImage: 'null',
pytorchJobImage: 'gcr.io/kubeflow-images-public/pytorch-operator:v0.5.0',
},
tensorboard: {
defaultTbImage: 'tensorflow/tensorflow:1.8.0',
logDir: 'logs',
name: 'tensorboard',
servicePort: 9000,
serviceType: 'ClusterIP',
targetPort: 6006,
},
"tf-job-operator": {
cloud: 'null',
deploymentNamespace: 'null',
deploymentScope: 'cluster',
name: 'tf-job-operator',
tfDefaultImage: 'null',
tfJobImage: 'gcr.io/kubeflow-images-public/tf_operator:v0.5.0',
tfJobUiServiceType: 'ClusterIP',
},
},
}

View File

@ -0,0 +1,14 @@
local env = std.extVar("__ksonnet/environments");
local params = std.extVar("__ksonnet/params").components.pipeline;
local k = import "k.libsonnet";
local pipelineBase = import "kubeflow/pipeline/pipeline.libsonnet";
// updatedParams includes the namespace from env by default.
local updatedParams = params + env;
local pipeline = pipelineBase {
params+: updatedParams,
};
std.prune(k.core.v1.list.new(pipeline.parts.all))

View File

@ -0,0 +1,7 @@
local env = std.extVar("__ksonnet/environments");
local params = std.extVar("__ksonnet/params").components["pytorch-operator"];
local k = import "k.libsonnet";
local operator = import "kubeflow/pytorch-job/pytorch-operator.libsonnet";
k.core.v1.list.new(operator.all(params, env))

View File

@ -0,0 +1,6 @@
local env = std.extVar("__ksonnet/environments");
local params = std.extVar("__ksonnet/params").components.tensorboard;
local tensorboard = import "kubeflow/tensorboard/tensorboard.libsonnet";
local instance = tensorboard.new(env, params);
instance.list(instance.all)

View File

@ -0,0 +1,6 @@
local env = std.extVar("__ksonnet/environments");
local params = std.extVar("__ksonnet/params").components["tf-job-operator"];
local tfJobOperator = import "kubeflow/tf-training/tf-job-operator.libsonnet";
local instance = tfJobOperator.new(env, params);
instance.list(instance.all)

View File

@ -0,0 +1,4 @@
local components = std.extVar("__ksonnet/components");
components + {
// Insert user-specified overrides here.
}

View File

@ -0,0 +1,9 @@
local base = import "base.libsonnet";
// uncomment if you reference ksonnet-lib
// local k = import "k.libsonnet";
// local deployment = k.apps.v1beta2.deployment;
base + {
// Insert user-specified overrides here. For example if a component is named \"nginx-deployment\", you might have something like:\n")
// "nginx-deployment"+: deployment.mixin.metadata.withLabels({foo: "bar"})
}

View File

@ -0,0 +1,17 @@
local params = std.extVar("__ksonnet/params");
local globals = import "globals.libsonnet";
local envParams = params + {
components +: {
// Insert component parameter overrides here. Ex:
// guestbook +: {
// name: "guestbook-dev",
// replicas: params.global.replicas,
// },
},
};
{
components: {
[x]: envParams.components[x] + globals, for x in std.objectFields(envParams.components)
},
}

View File

@ -0,0 +1,3 @@
approvers:
- IronPan
reviewers:

View File

@ -0,0 +1,35 @@
# Argo
> Prototypes for deploying Argo and running Argo Workflows
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [Quickstart](#quickstart)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## Quickstart
*The following commands use the `io.ksonnet.pkg.argo` prototype to deploy the Argo Workflow operator on your Kubernetes cluster*
First, create a cluster and install the ksonnet CLI (see root-level [README.md](../../README.md)).
If you haven't yet created a [ksonnet application](https://ksonnet.io/docs/tutorial#1-initialize-your-app), do so using `ks init <app-name>`.
Finally, in the ksonnet application directory, run the following:
```shell
# Install the kubeflow argo package
$ ks pkg install kubeflow/argo
# Expand prototype as a Jsonnet file, place in a file in the
# `components/` directory. (YAML and JSON are also available.)
$ ks prototype use io.ksonnet.pkg.argo argo \
--namespace default \
--name argo
# Apply to server.
$ ks apply default -c argo
```

View File

@ -0,0 +1,485 @@
{
// TODO(jlewi): Do we need to add parts corresponding to a service account and cluster binding role?
// see https://github.com/argoproj/argo/blob/master/cmd/argo/commands/install.go
local k = import "k.libsonnet",
local util = import "kubeflow/common/util.libsonnet",
new(_env, _params):: {
local params = _params + _env,
// CRD's are not namespace scoped; see
// https://kubernetes.io/docs/tasks/access-kubernetes-api/extend-api-custom-resource-definitions/
local workflowCRD = {
apiVersion: "apiextensions.k8s.io/v1beta1",
kind: "CustomResourceDefinition",
metadata: {
name: "workflows.argoproj.io",
},
spec: {
group: "argoproj.io",
names: {
kind: "Workflow",
listKind: "WorkflowList",
plural: "workflows",
shortNames: [
"wf",
],
singular: "workflow",
},
scope: "Namespaced",
version: "v1alpha1",
},
}, // crd
workflowCRD:: workflowCRD,
// Deploy the controller
local workflowController = {
apiVersion: "extensions/v1beta1",
kind: "Deployment",
metadata: {
labels: {
app: "workflow-controller",
},
name: "workflow-controller",
namespace: params.namespace,
},
spec: {
progressDeadlineSeconds: 600,
replicas: 1,
revisionHistoryLimit: 10,
selector: {
matchLabels: {
app: "workflow-controller",
},
},
strategy: {
rollingUpdate: {
maxSurge: "25%",
maxUnavailable: "25%",
},
type: "RollingUpdate",
},
template: {
metadata: {
creationTimestamp: null,
labels: {
app: "workflow-controller",
},
},
spec: {
containers: [
{
args: [
"--configmap",
"workflow-controller-configmap",
],
command: [
"workflow-controller",
],
env: [
{
name: "ARGO_NAMESPACE",
valueFrom: {
fieldRef: {
apiVersion: "v1",
fieldPath: "metadata.namespace",
},
},
},
],
image: params.workflowControllerImage,
imagePullPolicy: "IfNotPresent",
name: "workflow-controller",
resources: {},
terminationMessagePath: "/dev/termination-log",
terminationMessagePolicy: "File",
},
],
dnsPolicy: "ClusterFirst",
restartPolicy: "Always",
schedulerName: "default-scheduler",
securityContext: {},
serviceAccount: "argo",
serviceAccountName: "argo",
terminationGracePeriodSeconds: 30,
},
},
},
}, // deploy
workflowController:: workflowController,
local argoUI = {
apiVersion: "extensions/v1beta1",
kind: "Deployment",
metadata: {
labels: {
app: "argo-ui",
},
name: "argo-ui",
namespace: params.namespace,
},
spec: {
progressDeadlineSeconds: 600,
replicas: 1,
revisionHistoryLimit: 10,
selector: {
matchLabels: {
app: "argo-ui",
},
},
strategy: {
rollingUpdate: {
maxSurge: "25%",
maxUnavailable: "25%",
},
type: "RollingUpdate",
},
template: {
metadata: {
creationTimestamp: null,
labels: {
app: "argo-ui",
},
},
spec: {
containers: [
{
env: [
{
name: "ARGO_NAMESPACE",
valueFrom: {
fieldRef: {
apiVersion: "v1",
fieldPath: "metadata.namespace",
},
},
},
{
name: "IN_CLUSTER",
value: "true",
},
{
name: "BASE_HREF",
value: "/argo/",
},
],
image: params.uiImage,
imagePullPolicy: "IfNotPresent",
name: "argo-ui",
resources: {},
terminationMessagePath: "/dev/termination-log",
terminationMessagePolicy: "File",
readinessProbe: {
httpGet: {
path: "/",
port: 8001,
},
},
},
],
dnsPolicy: "ClusterFirst",
restartPolicy: "Always",
schedulerName: "default-scheduler",
securityContext: {},
serviceAccount: "argo-ui",
serviceAccountName: "argo-ui",
terminationGracePeriodSeconds: 30,
},
},
},
}, // deployUi
argoUI:: argoUI,
local argUIService = {
apiVersion: "v1",
kind: "Service",
metadata: {
labels: {
app: "argo-ui",
},
name: "argo-ui",
namespace: params.namespace,
annotations: {
"getambassador.io/config":
std.join("\n", [
"---",
"apiVersion: ambassador/v0",
"kind: Mapping",
"name: argo-ui-mapping",
"prefix: /argo/",
"service: argo-ui." + params.namespace,
]),
}, //annotations
},
spec: {
ports: [
{
port: 80,
targetPort: 8001,
},
],
selector: {
app: "argo-ui",
},
sessionAffinity: "None",
type: "NodePort",
},
},
argUIService:: argUIService,
local workflowControllerConfigmap = {
apiVersion: "v1",
data: {
config: std.format(|||
{
executorImage: %s,
artifactRepository:
{
s3: {
bucket: %s,
keyPrefix: %s,
endpoint: %s,
insecure: %s,
accessKeySecret: {
name: %s,
key: %s
},
secretKeySecret: {
name: %s,
key: %s
}
}
}
}
|||,
[
params.executorImage,
params.artifactRepositoryBucket,
params.artifactRepositoryKeyPrefix,
params.artifactRepositoryEndpoint,
params.artifactRepositoryInsecure,
params.artifactRepositoryAccessKeySecretName,
params.artifactRepositoryAccessKeySecretKey,
params.artifactRepositorySecretKeySecretName,
params.artifactRepositorySecretKeySecretKey,
]),
},
kind: "ConfigMap",
metadata: {
name: "workflow-controller-configmap",
namespace: params.namespace,
},
},
workflowControllerConfigmap:: workflowControllerConfigmap,
local argoServiceAccount = {
apiVersion: "v1",
kind: "ServiceAccount",
metadata: {
name: "argo",
namespace: params.namespace,
},
}, // service account
argoServiceAccount:: argoServiceAccount,
// Keep in sync with https://github.com/argoproj/argo/blob/master/cmd/argo/commands/const.go#L20
// Permissions need to be cluster wide for the workflow controller to be able to process workflows
// in other namespaces. We could potentially use the ConfigMap of the workflow-controller to
// scope it to a particular namespace in which case we might be able to restrict the permissions
// to a particular namespace.
local argoClusterRole = {
apiVersion: "rbac.authorization.k8s.io/v1beta1",
kind: "ClusterRole",
metadata: {
labels: {
app: "argo",
},
name: "argo",
},
rules: [
{
apiGroups: [""],
resources: [
"pods",
"pods/exec",
],
verbs: [
"create",
"get",
"list",
"watch",
"update",
"patch",
],
},
{
apiGroups: [""],
resources: [
"configmaps",
],
verbs: [
"get",
"watch",
"list",
],
},
{
apiGroups: [
"",
],
resources: [
"persistentvolumeclaims",
],
verbs: [
"create",
"delete",
],
},
{
apiGroups: [
"argoproj.io",
],
resources: [
"workflows",
],
verbs: [
"get",
"list",
"watch",
"update",
"patch",
],
},
],
}, // operator-role
argoClusterRole:: argoClusterRole,
local argoClusterRoleBinding = {
apiVersion: "rbac.authorization.k8s.io/v1beta1",
kind: "ClusterRoleBinding",
metadata: {
labels: {
app: "argo",
},
name: "argo",
},
roleRef: {
apiGroup: "rbac.authorization.k8s.io",
kind: "ClusterRole",
name: "argo",
},
subjects: [
{
kind: "ServiceAccount",
name: "argo",
namespace: params.namespace,
},
],
}, // role binding
argoClusterRoleBinding:: argoClusterRoleBinding,
local argoUIServiceAccount = {
apiVersion: "v1",
kind: "ServiceAccount",
metadata: {
name: "argo-ui",
namespace: params.namespace,
},
}, // service account
argoUIServiceAccount:: argoUIServiceAccount,
// Keep in sync with https://github.com/argoproj/argo/blob/master/cmd/argo/commands/const.go#L44
// Permissions need to be cluster wide for the workflow controller to be able to process workflows
// in other namespaces. We could potentially use the ConfigMap of the workflow-controller to
// scope it to a particular namespace in which case we might be able to restrict the permissions
// to a particular namespace.
local argoUIRole = {
apiVersion: "rbac.authorization.k8s.io/v1beta1",
kind: "ClusterRole",
metadata: {
labels: {
app: "argo",
},
name: "argo-ui",
},
rules: [
{
apiGroups: [""],
resources: [
"pods",
"pods/exec",
"pods/log",
],
verbs: [
"get",
"list",
"watch",
],
},
{
apiGroups: [""],
resources: [
"secrets",
],
verbs: [
"get",
],
},
{
apiGroups: [
"argoproj.io",
],
resources: [
"workflows",
],
verbs: [
"get",
"list",
"watch",
],
},
],
}, // operator-role
argoUIRole:: argoUIRole,
local argUIClusterRoleBinding = {
apiVersion: "rbac.authorization.k8s.io/v1beta1",
kind: "ClusterRoleBinding",
metadata: {
labels: {
app: "argo-ui",
},
name: "argo-ui",
},
roleRef: {
apiGroup: "rbac.authorization.k8s.io",
kind: "ClusterRole",
name: "argo-ui",
},
subjects: [
{
kind: "ServiceAccount",
name: "argo-ui",
namespace: params.namespace,
},
],
}, // role binding
argUIClusterRoleBinding:: argUIClusterRoleBinding,
parts: self,
all:: [
self.workflowCRD,
self.workflowController,
self.argoUI,
self.argUIService,
self.workflowControllerConfigmap,
self.argoServiceAccount,
self.argoClusterRole,
self.argoClusterRoleBinding,
self.argoUIServiceAccount,
self.argoUIRole,
self.argUIClusterRoleBinding,
],
list(obj=self.all):: util.list(obj),
},
}

View File

@ -0,0 +1,35 @@
{
"name": "argo",
"apiVersion": "0.0.1",
"kind": "ksonnet.io/parts",
"description": "Prototypes for running Argo workflows.\n",
"author": "kubeflow team <kubeflow-team@google.com>",
"contributors": [
{
"name": "Jeremy Lewi",
"email": "jlewi@google.com"
}
],
"repository": {
"type": "git",
"url": "https://github.com/kubeflow/kubeflow"
},
"bugs": {
"url": "https://github.com/kubeflow/kubeflow/issues"
},
"keywords": [
"kubeflow",
"argo",
"workflows"
],
"quickStart": {
"prototype": "io.ksonnet.pkg.argo",
"componentName": "argo",
"flags": {
"name": "argo",
"namespace": "",
},
"comment": "Deploy Argo"
},
"license": "Apache 2.0"
}

View File

@ -0,0 +1,20 @@
// @apiVersion 0.1
// @name io.ksonnet.pkg.argo
// @description Deploy Argo workflow engine
// @shortDescription Argo workflow engine
// @param name string Name to give to the component
// @optionalParam workflowControllerImage string argoproj/workflow-controller:v2.2.0 workflowControllerImage
// @optionalParam uiImage string argoproj/argoui:v2.2.0 uiImage
// @optionalParam executorImage string argoproj/argoexec:v2.2.0 executorImage
// @optionalParam artifactRepositoryKeyPrefix string artifacts artifactRepositoryKeyPrefix
// @optionalParam artifactRepositoryEndpoint string minio-service.kubeflow:9000 artifactRepositoryEndpoint
// @optionalParam artifactRepositoryBucket string mlpipeline artifactRepositoryBucket
// @optionalParam artifactRepositoryInsecure string true artifactRepositoryInsecure
// @optionalParam artifactRepositoryAccessKeySecretName string mlpipeline-minio-artifact artifactRepositoryAccessKeySecretName
// @optionalParam artifactRepositoryAccessKeySecretKey string accesskey artifactRepositoryAccessKeySecretKey
// @optionalParam artifactRepositorySecretKeySecretName string mlpipeline-minio-artifact artifactRepositorySecretKeySecretName
// @optionalParam artifactRepositorySecretKeySecretKey string secretkey artifactRepositorySecretKeySecretKey
local argo = import "kubeflow/argo/argo.libsonnet";
local instance = argo.new(env, params);
instance.list(instance.all)

View File

@ -0,0 +1,452 @@
local argo = import "kubeflow/argo/argo.libsonnet";
local testSuite = import "kubeflow/common/testsuite.libsonnet";
local params = {
name: "argo",
workflowControllerImage: "argoproj/workflow-controller:v2.2.0",
uiImage: "argoproj/argoui:v2.2.0",
executorImage: "argoproj/argoexec:v2.2.0",
artifactRepositoryKeyPrefix: "artifacts",
artifactRepositoryEndpoint: "minio-service.kubeflow:9000",
artifactRepositoryBucket: "mlpipeline",
artifactRepositoryInsecure: "true",
artifactRepositoryAccessKeySecretName: "mlpipeline-minio-artifact",
artifactRepositoryAccessKeySecretKey: "accesskey",
artifactRepositorySecretKeySecretName: "mlpipeline-minio-artifact",
artifactRepositorySecretKeySecretKey: "secretkey",
};
local env = {
namespace: "kubeflow",
};
local instance = argo.new(env, params);
local testCases = [
{
actual: instance.parts.workflowCRD,
expected: {
apiVersion: "apiextensions.k8s.io/v1beta1",
kind: "CustomResourceDefinition",
metadata: {
name: "workflows.argoproj.io",
},
spec: {
group: "argoproj.io",
names: {
kind: "Workflow",
listKind: "WorkflowList",
plural: "workflows",
shortNames: [
"wf",
],
singular: "workflow",
},
scope: "Namespaced",
version: "v1alpha1",
},
},
},
{
actual: instance.parts.workflowController,
expected: {
apiVersion: "extensions/v1beta1",
kind: "Deployment",
metadata: {
labels: {
app: "workflow-controller",
},
name: "workflow-controller",
namespace: "kubeflow",
},
spec: {
progressDeadlineSeconds: 600,
replicas: 1,
revisionHistoryLimit: 10,
selector: {
matchLabels: {
app: "workflow-controller",
},
},
strategy: {
rollingUpdate: {
maxSurge: "25%",
maxUnavailable: "25%",
},
type: "RollingUpdate",
},
template: {
metadata: {
creationTimestamp: null,
labels: {
app: "workflow-controller",
},
},
spec: {
containers: [
{
args: [
"--configmap",
"workflow-controller-configmap",
],
command: [
"workflow-controller",
],
env: [
{
name: "ARGO_NAMESPACE",
valueFrom: {
fieldRef: {
apiVersion: "v1",
fieldPath: "metadata.namespace",
},
},
},
],
image: "argoproj/workflow-controller:v2.2.0",
imagePullPolicy: "IfNotPresent",
name: "workflow-controller",
resources: {},
terminationMessagePath: "/dev/termination-log",
terminationMessagePolicy: "File",
},
],
dnsPolicy: "ClusterFirst",
restartPolicy: "Always",
schedulerName: "default-scheduler",
securityContext: {},
serviceAccount: "argo",
serviceAccountName: "argo",
terminationGracePeriodSeconds: 30,
},
},
},
},
},
{
actual: instance.parts.argoUI,
expected: {
apiVersion: "extensions/v1beta1",
kind: "Deployment",
metadata: {
labels: {
app: "argo-ui",
},
name: "argo-ui",
namespace: "kubeflow",
},
spec: {
progressDeadlineSeconds: 600,
replicas: 1,
revisionHistoryLimit: 10,
selector: {
matchLabels: {
app: "argo-ui",
},
},
strategy: {
rollingUpdate: {
maxSurge: "25%",
maxUnavailable: "25%",
},
type: "RollingUpdate",
},
template: {
metadata: {
creationTimestamp: null,
labels: {
app: "argo-ui",
},
},
spec: {
containers: [
{
env: [
{
name: "ARGO_NAMESPACE",
valueFrom: {
fieldRef: {
apiVersion: "v1",
fieldPath: "metadata.namespace",
},
},
},
{
name: "IN_CLUSTER",
value: "true",
},
{
name: "BASE_HREF",
value: "/argo/",
},
],
image: "argoproj/argoui:v2.2.0",
imagePullPolicy: "IfNotPresent",
name: "argo-ui",
readinessProbe: {
httpGet: {
path: "/",
port: 8001,
},
},
resources: {},
terminationMessagePath: "/dev/termination-log",
terminationMessagePolicy: "File",
},
],
dnsPolicy: "ClusterFirst",
restartPolicy: "Always",
schedulerName: "default-scheduler",
securityContext: {},
serviceAccount: "argo-ui",
serviceAccountName: "argo-ui",
terminationGracePeriodSeconds: 30,
},
},
},
},
},
{
actual: instance.parts.argUIService,
expected: {
apiVersion: "v1",
kind: "Service",
metadata: {
annotations: {
"getambassador.io/config": "---\napiVersion: ambassador/v0\nkind: Mapping\nname: argo-ui-mapping\nprefix: /argo/\nservice: argo-ui.kubeflow",
},
labels: {
app: "argo-ui",
},
name: "argo-ui",
namespace: "kubeflow",
},
spec: {
ports: [
{
port: 80,
targetPort: 8001,
},
],
selector: {
app: "argo-ui",
},
sessionAffinity: "None",
type: "NodePort",
},
},
},
{
actual: instance.parts.workflowControllerConfigmap,
expected: {
apiVersion: "v1",
data: {
config: "{\nexecutorImage: argoproj/argoexec:v2.2.0,\nartifactRepository:\n{\n s3: {\n bucket: mlpipeline,\n keyPrefix: artifacts,\n endpoint: minio-service.kubeflow:9000,\n insecure: true,\n accessKeySecret: {\n name: mlpipeline-minio-artifact,\n key: accesskey\n },\n secretKeySecret: {\n name: mlpipeline-minio-artifact,\n key: secretkey\n }\n }\n}\n}\n",
},
kind: "ConfigMap",
metadata: {
name: "workflow-controller-configmap",
namespace: "kubeflow",
},
},
},
{
actual: instance.parts.argoServiceAccount,
expected: {
apiVersion: "v1",
kind: "ServiceAccount",
metadata: {
name: "argo",
namespace: "kubeflow",
},
},
},
{
actual: instance.parts.argoClusterRole,
expected: {
apiVersion: "rbac.authorization.k8s.io/v1beta1",
kind: "ClusterRole",
metadata: {
labels: {
app: "argo",
},
name: "argo",
},
rules: [
{
apiGroups: [
"",
],
resources: [
"pods",
"pods/exec",
],
verbs: [
"create",
"get",
"list",
"watch",
"update",
"patch",
],
},
{
apiGroups: [
"",
],
resources: [
"configmaps",
],
verbs: [
"get",
"watch",
"list",
],
},
{
apiGroups: [
"",
],
resources: [
"persistentvolumeclaims",
],
verbs: [
"create",
"delete",
],
},
{
apiGroups: [
"argoproj.io",
],
resources: [
"workflows",
],
verbs: [
"get",
"list",
"watch",
"update",
"patch",
],
},
],
},
},
{
actual: instance.parts.argoClusterRoleBinding,
expected: {
apiVersion: "rbac.authorization.k8s.io/v1beta1",
kind: "ClusterRoleBinding",
metadata: {
labels: {
app: "argo",
},
name: "argo",
},
roleRef: {
apiGroup: "rbac.authorization.k8s.io",
kind: "ClusterRole",
name: "argo",
},
subjects: [
{
kind: "ServiceAccount",
name: "argo",
namespace: "kubeflow",
},
],
},
},
{
actual: instance.parts.argoUIServiceAccount,
expected: {
apiVersion: "v1",
kind: "ServiceAccount",
metadata: {
name: "argo-ui",
namespace: "kubeflow",
},
},
},
{
actual: instance.parts.argoUIRole,
expected: {
apiVersion: "rbac.authorization.k8s.io/v1beta1",
kind: "ClusterRole",
metadata: {
labels: {
app: "argo",
},
name: "argo-ui",
},
rules: [
{
apiGroups: [
"",
],
resources: [
"pods",
"pods/exec",
"pods/log",
],
verbs: [
"get",
"list",
"watch",
],
},
{
apiGroups: [
"",
],
resources: [
"secrets",
],
verbs: [
"get",
],
},
{
apiGroups: [
"argoproj.io",
],
resources: [
"workflows",
],
verbs: [
"get",
"list",
"watch",
],
},
],
},
},
{
actual: instance.parts.argUIClusterRoleBinding,
expected: {
apiVersion: "rbac.authorization.k8s.io/v1beta1",
kind: "ClusterRoleBinding",
metadata: {
labels: {
app: "argo-ui",
},
name: "argo-ui",
},
roleRef: {
apiGroup: "rbac.authorization.k8s.io",
kind: "ClusterRole",
name: "argo-ui",
},
subjects: [
{
kind: "ServiceAccount",
name: "argo-ui",
namespace: "kubeflow",
},
],
},
},
];
testSuite.run(testCases)

View File

@ -0,0 +1,3 @@
approvers:
- gaocegege
reviewers:

View File

@ -0,0 +1,11 @@
# common
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [common](#common)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
This ksonnet package contains kubeflow common prototypes such as ambassador, spartakus, etc. You can install this using `ks pkg install kubeflow/common`. `ks prototype list` should list the available prototypes. `ks prototype describe <name>` should describe the prototype.

View File

@ -0,0 +1,226 @@
{
local k = import "k.libsonnet",
local util = import "kubeflow/common/util.libsonnet",
new(_env, _params):: {
local params = _params + _env,
local ambassadorService = {
apiVersion: "v1",
kind: "Service",
metadata: {
labels: {
service: "ambassador",
},
name: "ambassador",
namespace: params.namespace,
},
spec: {
ports: [
{
name: "ambassador",
port: 80,
targetPort: 80,
[if (params.ambassadorServiceType == 'NodePort') &&
(params.ambassadorNodePort >= 30000) &&
(params.ambassadorNodePort <= 32767)
then 'nodePort']: params.ambassadorNodePort,
},
],
selector: {
service: "ambassador",
},
type: params.ambassadorServiceType,
},
}, // service
ambassadorService:: ambassadorService,
local adminService = {
apiVersion: "v1",
kind: "Service",
metadata: {
labels: {
service: "ambassador-admin",
},
name: "ambassador-admin",
namespace: params.namespace,
},
spec: {
ports: [
{
name: "ambassador-admin",
port: 8877,
targetPort: 8877,
},
],
selector: {
service: "ambassador",
},
type: "ClusterIP",
},
}, // adminService
adminService:: adminService,
local ambassadorRole = {
apiVersion: "rbac.authorization.k8s.io/v1beta1",
kind: "ClusterRole",
metadata: {
name: "ambassador",
},
rules: [
{
apiGroups: [
"",
],
resources: [
"services",
],
verbs: [
"get",
"list",
"watch",
],
},
{
apiGroups: [
"",
],
resources: [
"configmaps",
],
verbs: [
"create",
"update",
"patch",
"get",
"list",
"watch",
],
},
{
apiGroups: [
"",
],
resources: [
"secrets",
],
verbs: [
"get",
"list",
"watch",
],
},
],
}, // role
ambassadorRole:: ambassadorRole,
local ambassadorServiceAccount = {
apiVersion: "v1",
kind: "ServiceAccount",
metadata: {
name: "ambassador",
namespace: params.namespace,
},
}, // serviceAccount
ambassadorServiceAccount:: ambassadorServiceAccount,
local ambassadorRoleBinding = {
apiVersion: "rbac.authorization.k8s.io/v1beta1",
kind: "ClusterRoleBinding",
metadata: {
name: "ambassador",
},
roleRef: {
apiGroup: "rbac.authorization.k8s.io",
kind: "ClusterRole",
name: "ambassador",
},
subjects: [
{
kind: "ServiceAccount",
name: "ambassador",
namespace: params.namespace,
},
],
}, // roleBinding
ambassadorRoleBinding:: ambassadorRoleBinding,
local ambassadorDeployment = {
apiVersion: "apps/v1beta1",
kind: "Deployment",
metadata: {
name: "ambassador",
namespace: params.namespace,
},
spec: {
replicas: params.replicas,
template: {
metadata: {
labels: {
service: "ambassador",
},
namespace: params.namespace,
},
spec: {
containers: [
{
env: [
{
name: "AMBASSADOR_NAMESPACE",
valueFrom: {
fieldRef: {
fieldPath: "metadata.namespace",
},
},
},
],
image: params.ambassadorImage,
name: "ambassador",
resources: {
limits: {
cpu: 1,
memory: "400Mi",
},
requests: {
cpu: "200m",
memory: "100Mi",
},
},
readinessProbe: {
httpGet: {
path: "/ambassador/v0/check_ready",
port: 8877,
},
initialDelaySeconds: 30,
periodSeconds: 30,
},
livenessProbe: {
httpGet: {
path: "/ambassador/v0/check_alive",
port: 8877,
},
initialDelaySeconds: 30,
periodSeconds: 30,
},
},
],
restartPolicy: "Always",
serviceAccountName: "ambassador",
},
},
},
}, // deploy
ambassadorDeployment:: ambassadorDeployment,
parts:: self,
all:: [
self.ambassadorService,
self.adminService,
self.ambassadorRole,
self.ambassadorServiceAccount,
self.ambassadorRoleBinding,
self.ambassadorDeployment,
],
list(obj=self.all):: util.list(obj),
},
}

View File

@ -0,0 +1,197 @@
{
local util = import "kubeflow/common/util.libsonnet",
new(_env, _params):: {
local params = _params + _env,
local ui_name = params.name + "-login",
local authService = {
apiVersion: "v1",
kind: "Service",
metadata: {
labels: {
app: params.name,
},
name: params.name,
namespace: params.namespace,
annotations: {
"getambassador.io/config":
std.join("\n", [
"---",
"apiVersion: ambassador/v0",
"kind: AuthService",
"name: " + params.name,
"auth_service: " + params.name + "." + params.namespace + ":8085",
'allowed_headers:\n- "x-from-login"',
]),
}, //annotations
},
spec: {
ports: [
{
port: 8085,
targetPort: 8085,
},
],
selector: {
app: params.name,
},
type: "ClusterIP",
},
},
authService:: authService,
local authDeployment = {
apiVersion: "extensions/v1beta1",
kind: "Deployment",
metadata: {
name: params.name,
namespace: params.namespace,
},
spec: {
// replicas here should always be 1:
// we store auth cookies in memory and we don't support share them among pods.
replicas: 1,
strategy: {
type: "RollingUpdate",
},
template: {
metadata: {
labels: {
app: params.name,
},
},
spec: {
containers: [
{
image: params.image,
name: "app",
workingDir: "/opt/kubeflow",
env: [
{
name: "USERNAME",
valueFrom: {
secretKeyRef: {
name: params.authSecretName,
key: "username",
},
},
},
{
name: "PASSWORDHASH",
valueFrom: {
secretKeyRef: {
name: params.authSecretName,
key: "passwordhash",
},
},
},
],
command: [
"/opt/kubeflow/gatekeeper",
],
args: [
"--username=$(USERNAME)",
"--pwhash=$(PASSWORDHASH)",
],
ports: [
{
containerPort: 8085,
},
],
},
],
},
},
},
},
authDeployment:: authDeployment,
local loginService = {
apiVersion: "v1",
kind: "Service",
metadata: {
labels: {
app: ui_name,
},
name: ui_name,
namespace: params.namespace,
annotations: {
"getambassador.io/config":
std.join("\n", [
"---",
"apiVersion: ambassador/v0",
"kind: Mapping",
"name: kflogin-mapping",
"prefix: /kflogin",
"rewrite: /kflogin",
"timeout_ms: 300000",
"service: " + ui_name + "." + params.namespace,
"use_websocket: true",
]),
}, //annotations
},
spec: {
ports: [
{
port: 80,
targetPort: 5000,
},
],
selector: {
app: ui_name,
},
type: "ClusterIP",
},
},
loginService:: loginService,
local loginDeployment = {
apiVersion: "extensions/v1beta1",
kind: "Deployment",
metadata: {
name: ui_name,
namespace: params.namespace,
},
spec: {
replicas: 1,
strategy: {
type: "RollingUpdate",
},
template: {
metadata: {
labels: {
app: ui_name,
},
},
spec: {
containers: [
{
image: params.imageui,
name: "app",
ports: [
{
containerPort: 5000,
},
],
},
],
},
},
},
},
loginDeployment:: loginDeployment,
parts:: self,
all:: [
self.authService,
self.authDeployment,
self.loginService,
self.loginDeployment,
],
list(obj=self.all):: util.list(obj),
},
}

View File

@ -0,0 +1,221 @@
{
local k = import "k.libsonnet",
local util = import "kubeflow/common/util.libsonnet",
new(_env, _params):: {
local params = _params + _env,
local centralDashboardDeployment = {
apiVersion: "extensions/v1beta1",
kind: "Deployment",
metadata: {
labels: {
app: "centraldashboard",
},
name: "centraldashboard",
namespace: params.namespace,
},
spec: {
template: {
metadata: {
labels: {
app: "centraldashboard",
},
},
spec: {
containers: [
{
image: params.image,
name: "centraldashboard",
ports: [
{
containerPort: 8082,
},
],
},
],
serviceAccountName: "centraldashboard",
},
},
},
}, // deployUi
centralDashboardDeployment:: centralDashboardDeployment,
local centralDashboardService = {
// Due to https://github.com/ksonnet/ksonnet/issues/670, escaped characters in
// jsonnet files are not interpreted correctly by ksonnet, which causes runtime
// parsing failures. This is fixed in ksonnet 0.12.0, so we can merge this back
// to the jsonnet file when we take a dependency on ksonnet 0.12.0 or later.
local annotations = function(namespace) {
"getambassador.io/config":
std.join("\n", [
"---",
"apiVersion: ambassador/v0",
"kind: Mapping",
"name: centralui-mapping",
"prefix: /",
"rewrite: /",
"service: centraldashboard." + namespace,
]),
},
apiVersion: "v1",
kind: "Service",
metadata: {
labels: {
app: "centraldashboard",
},
name: "centraldashboard",
namespace: params.namespace,
annotations: annotations(params.namespace),
},
spec: {
ports: [
{
port: 80,
targetPort: 8082,
},
],
selector: {
app: "centraldashboard",
},
sessionAffinity: "None",
type: "ClusterIP",
},
}, //service
centralDashboardService:: centralDashboardService,
local centralDashboardServiceAccount = {
apiVersion: "v1",
kind: "ServiceAccount",
metadata: {
name: "centraldashboard",
namespace: params.namespace,
},
}, // service account
centralDashboardServiceAccount:: centralDashboardServiceAccount,
local centralDashboardRole = {
apiVersion: "rbac.authorization.k8s.io/v1beta1",
kind: "Role",
metadata: {
labels: {
app: "centraldashboard",
},
name: "centraldashboard",
namespace: params.namespace,
},
rules: [
{
apiGroups: [""],
resources: [
"pods",
"pods/exec",
"pods/log",
],
verbs: [
"get",
"list",
"watch",
],
},
{
apiGroups: [""],
resources: [
"secrets",
],
verbs: [
"get",
],
},
],
}, // role
centralDashboardRole:: centralDashboardRole,
local centralDashboardRoleBinding = {
apiVersion: "rbac.authorization.k8s.io/v1beta1",
kind: "RoleBinding",
metadata: {
labels: {
app: "centraldashboard",
},
name: "centraldashboard",
namespace: params.namespace,
},
roleRef: {
apiGroup: "rbac.authorization.k8s.io",
kind: "Role",
name: "centraldashboard",
},
subjects: [
{
kind: "ServiceAccount",
name: "centraldashboard",
namespace: params.namespace,
},
],
}, // role binding
centralDashboardRoleBinding:: centralDashboardRoleBinding,
local centralDashboardClusterRole = {
apiVersion: "rbac.authorization.k8s.io/v1",
kind: "ClusterRole",
metadata: {
labels: {
app: "centraldashboard",
},
name: "centraldashboard",
},
rules: [
{
apiGroups: [""],
resources: [
"namespaces",
"events"
],
verbs: [
"get",
"list",
"watch",
],
}
],
}, // clusterrole
centralDashboardClusterRole:: centralDashboardClusterRole,
local centralDashboardClusterRoleBinding = {
apiVersion: "rbac.authorization.k8s.io/v1",
kind: "ClusterRoleBinding",
metadata: {
labels: {
app: "centraldashboard",
},
name: "centraldashboard",
},
roleRef: {
apiGroup: "rbac.authorization.k8s.io",
kind: "ClusterRole",
name: "centraldashboard",
},
subjects: [
{
kind: "ServiceAccount",
name: "centraldashboard",
namespace: params.namespace,
},
],
}, // clusterrolebinding
centralDashboardClusterRoleBinding:: centralDashboardClusterRoleBinding,
parts:: self,
all:: [
self.centralDashboardDeployment,
self.centralDashboardService,
self.centralDashboardServiceAccount,
self.centralDashboardRole,
self.centralDashboardRoleBinding,
self.centralDashboardClusterRole,
self.centralDashboardClusterRoleBinding,
],
list(obj=self.all):: util.list(obj),
},
}

View File

@ -0,0 +1,94 @@
{
local util = import "kubeflow/common/util.libsonnet",
new(_env, _params):: {
local params = _params + _env,
local service = {
apiVersion: "v1",
kind: "Service",
metadata: {
labels: {
app: params.name,
},
name: params.name,
namespace: params.namespace,
annotations: {
"getambassador.io/config":
std.join("\n", [
"---",
"apiVersion: ambassador/v0",
"kind: Mapping",
"name: " + params.name + "-mapping",
"prefix: /" + params.name,
"rewrite: /",
"service: " + params.name + "." + params.namespace,
]),
}, //annotations
},
spec: {
ports: [
{
port: 80,
targetPort: 8080,
},
],
selector: {
app: params.name,
},
type: "ClusterIP",
},
},
service:: service,
local deployment = {
apiVersion: "extensions/v1beta1",
kind: "Deployment",
metadata: {
name: params.name,
namespace: params.namespace,
},
spec: {
replicas: 1,
template: {
metadata: {
labels: {
app: params.name,
},
},
spec: {
containers: [
{
image: params.image,
name: "app",
ports: [
{
containerPort: 8080,
},
],
readinessProbe: {
httpGet: {
path: "/headers",
port: 8080,
},
initialDelaySeconds: 5,
periodSeconds: 30,
},
},
],
},
},
},
},
deployment:: deployment,
parts:: self,
all:: [
self.service,
self.deployment,
],
list(obj=self.all):: util.list(obj),
},
}

View File

@ -0,0 +1,35 @@
{
"name": "common",
"apiVersion": "0.0.1",
"kind": "ksonnet.io/parts",
"description": "Common components of Kubeflow.\n",
"author": "kubeflow team <kubeflow-team@google.com>",
"contributors": [
{
"name": "Jeremy Lewi",
"email": "jlewi@google.com"
}
],
"repository": {
"type": "git",
"url": "https://github.com/kubeflow/kubeflow"
},
"bugs": {
"url": "https://github.com/kubeflow/kubeflow/issues"
},
"keywords": [
"kubeflow",
"tensorflow"
],
"quickStart": {
"prototype": "io.ksonnet.pkg.kubeflow",
"componentName": "common",
"flags": {
"name": "common",
"namespace": "default",
"disks": ""
},
"comment": "Common Kubeflow components."
},
"license": "Apache 2.0"
}

View File

@ -0,0 +1,14 @@
// @apiVersion 0.1
// @name io.ksonnet.pkg.ambassador
// @description Ambassador Component
// @shortDescription Ambassador
// @param name string Name
// @optionalParam platform string none supported platforms {none|gke|minikube}
// @optionalParam ambassadorServiceType string ClusterIP The service type for the API Gateway {ClusterIP|NodePort|LoadBalancer}.
// @optionalParam ambassadorNodePort number 0 Optional nodePort to use when ambassadorServiceType is NodePort {30000-32767}.
// @optionalParam ambassadorImage string quay.io/datawire/ambassador:0.37.0 The image for the API Gateway.
// @optionalParam replicas number 3 The number of replicas.
local ambassador = import "kubeflow/common/ambassador.libsonnet";
local instance = ambassador.new(env, params);
instance.list(instance.all)

View File

@ -0,0 +1,12 @@
// @apiVersion 0.1
// @name io.ksonnet.pkg.basic-auth
// @description Provides http basic auth for all ambassador traffic.
// @shortDescription Http basic auth.
// @param name string Name for the component
// @optionalParam authSecretName string kubeflow-login Contains username and passwordhash for UI/API auth.
// @optionalParam image string gcr.io/kubeflow-images-public/gatekeeper:v0.5.0 Auth service image to use.
// @optionalParam imageui string gcr.io/kubeflow-images-public/kflogin-ui:v0.5.0 UI image to use.
local basicauth = import "kubeflow/common/basic-auth.libsonnet";
local instance = basicauth.new(env, params);
instance.list(instance.all)

View File

@ -0,0 +1,10 @@
// @apiVersion 0.1
// @name io.ksonnet.pkg.centraldashboard
// @description centraldashboard Component
// @shortDescription centraldashboard
// @param name string Name
// @optionalParam image string gcr.io/kubeflow-images-public/centraldashboard:v0.5.0 Image for the central dashboard
local centraldashboard = import "kubeflow/common/centraldashboard.libsonnet";
local instance = centraldashboard.new(env, params);
instance.list(instance.all)

View File

@ -0,0 +1,10 @@
// @apiVersion 0.1
// @name io.ksonnet.pkg.echo-server
// @description Provides a simple server for testing connections; primarily IAP.
// @shortDescription A simple echo server.
// @param name string Name for the component
// @optionalParam image string gcr.io/kubeflow-images-staging/echo-server:v20180628-44f08d31 The image to use.
local echoserver = import "kubeflow/common/echo-server.libsonnet";
local instance = echoserver.new(env, params);
instance.list(instance.all)

View File

@ -0,0 +1,11 @@
// @apiVersion 0.1
// @name io.ksonnet.pkg.spartakus
// @description spartakus component for usage collection
// @shortDescription spartakus component for usage collection
// @param name string Name
// @optionalParam usageId string unknown_cluster Optional id to use when reporting usage to kubeflow.org
// @optionalParam reportUsage string false Whether or not to report Kubeflow usage to kubeflow.org.
local spartakus = import "kubeflow/common/spartakus.libsonnet";
local instance = spartakus.new(env, params);
instance.list(instance.all)

View File

@ -0,0 +1,122 @@
{
local k = import "k.libsonnet",
local util = import "kubeflow/common/util.libsonnet",
new(_env, _params):: {
local params = _params + _env {
reportUsageBool: util.toBool(_params.reportUsage),
},
// Spartakus needs to be able to get information about the cluster to create a report.
local clusterRole = {
apiVersion: "rbac.authorization.k8s.io/v1beta1",
kind: "ClusterRole",
metadata: {
labels: {
app: "spartakus",
},
name: "spartakus",
},
rules: [
{
apiGroups: [
"",
],
resources: [
"nodes",
],
verbs: [
"get",
"list",
],
},
],
}, // role
clusterRole:: clusterRole,
local clusterRoleBinding = {
apiVersion: "rbac.authorization.k8s.io/v1beta1",
kind: "ClusterRoleBinding",
metadata: {
labels: {
app: "spartakus",
},
name: "spartakus",
},
roleRef: {
apiGroup: "rbac.authorization.k8s.io",
kind: "ClusterRole",
name: "spartakus",
},
subjects: [
{
kind: "ServiceAccount",
name: "spartakus",
namespace: params.namespace,
},
],
}, // operator-role binding
clusterRoleBinding:: clusterRoleBinding,
local serviceAccount = {
apiVersion: "v1",
kind: "ServiceAccount",
metadata: {
labels: {
app: "spartakus",
},
name: "spartakus",
namespace: params.namespace,
},
},
serviceAccount:: serviceAccount,
local volunteer = {
apiVersion: "extensions/v1beta1",
kind: "Deployment",
metadata: {
name: "spartakus-volunteer",
namespace: params.namespace,
labels: {
app: "spartakus",
},
},
spec: {
replicas: 1,
template: {
metadata: {
labels: {
app: "spartakus-volunteer",
},
},
spec: {
containers: [
{
image: "gcr.io/google_containers/spartakus-amd64:v1.1.0",
name: "volunteer",
args: [
"volunteer",
"--cluster-id=" + params.usageId,
"--database=https://stats-collector.kubeflow.org",
],
},
],
serviceAccountName: "spartakus",
}, // spec
},
},
}, // deployment
volunteer:: volunteer,
parts:: self,
all:: if params.reportUsageBool then (
[
self.clusterRole,
self.clusterRoleBinding,
self.serviceAccount,
self.volunteer,
]
) else [],
list(obj=self.all):: util.list(obj),
},
}

View File

@ -0,0 +1,231 @@
local ambassador = import "kubeflow/common/ambassador.libsonnet";
local testSuite = import "kubeflow/common/testsuite.libsonnet";
local params = {
name: "ambassador",
platform: "gke",
ambassadorServiceType: "ClusterIP",
ambassadorImage: "quay.io/datawire/ambassador:0.37.0",
replicas: 3,
};
local env = {
namespace: "kubeflow",
};
local instance = ambassador.new(env, params);
local testCases = [
{
actual: instance.parts.ambassadorService,
expected:
{
apiVersion: "v1",
kind: "Service",
metadata: {
labels: {
service: "ambassador",
},
name: "ambassador",
namespace: "kubeflow",
},
spec: {
ports: [
{
name: "ambassador",
port: 80,
targetPort: 80,
},
],
selector: {
service: "ambassador",
},
type: "ClusterIP",
},
},
},
{
actual: instance.parts.adminService,
expected: {
apiVersion: "v1",
kind: "Service",
metadata: {
labels: {
service: "ambassador-admin",
},
name: "ambassador-admin",
namespace: "kubeflow",
},
spec: {
ports: [
{
name: "ambassador-admin",
port: 8877,
targetPort: 8877,
},
],
selector: {
service: "ambassador",
},
type: "ClusterIP",
},
},
},
{
actual: instance.parts.ambassadorRole,
expected: {
apiVersion: "rbac.authorization.k8s.io/v1beta1",
kind: "ClusterRole",
metadata: {
name: "ambassador",
},
rules: [
{
apiGroups: [
"",
],
resources: [
"services",
],
verbs: [
"get",
"list",
"watch",
],
},
{
apiGroups: [
"",
],
resources: [
"configmaps",
],
verbs: [
"create",
"update",
"patch",
"get",
"list",
"watch",
],
},
{
apiGroups: [
"",
],
resources: [
"secrets",
],
verbs: [
"get",
"list",
"watch",
],
},
],
},
},
{
actual: instance.parts.ambassadorServiceAccount,
expected: {
apiVersion: "v1",
kind: "ServiceAccount",
metadata: {
name: "ambassador",
namespace: "kubeflow",
},
},
},
{
actual: instance.parts.ambassadorRoleBinding,
expected: {
apiVersion: "rbac.authorization.k8s.io/v1beta1",
kind: "ClusterRoleBinding",
metadata: {
name: "ambassador",
},
roleRef: {
apiGroup: "rbac.authorization.k8s.io",
kind: "ClusterRole",
name: "ambassador",
},
subjects: [
{
kind: "ServiceAccount",
name: "ambassador",
namespace: "kubeflow",
},
],
},
},
{
actual: instance.parts.ambassadorDeployment,
expected:
{
apiVersion: "apps/v1beta1",
kind: "Deployment",
metadata: {
name: "ambassador",
namespace: "kubeflow",
},
spec: {
replicas: 3,
template: {
metadata: {
labels: {
service: "ambassador",
},
namespace: "kubeflow",
},
spec: {
containers: [
{
env: [
{
name: "AMBASSADOR_NAMESPACE",
valueFrom: {
fieldRef: {
fieldPath: "metadata.namespace",
},
},
},
],
image: "quay.io/datawire/ambassador:0.37.0",
livenessProbe: {
httpGet: {
path: "/ambassador/v0/check_alive",
port: 8877,
},
initialDelaySeconds: 30,
periodSeconds: 30,
},
name: "ambassador",
readinessProbe: {
httpGet: {
path: "/ambassador/v0/check_ready",
port: 8877,
},
initialDelaySeconds: 30,
periodSeconds: 30,
},
resources: {
limits: {
cpu: 1,
memory: "400Mi",
},
requests: {
cpu: "200m",
memory: "100Mi",
},
},
},
],
restartPolicy: "Always",
serviceAccountName: "ambassador",
},
},
},
},
},
];
testSuite.run(testCases)

View File

@ -0,0 +1,167 @@
local centraldashboard = import "../centraldashboard.libsonnet";
local testSuite = import "kubeflow/common/testsuite.libsonnet";
local params = {
image: "gcr.io/kubeflow-images-public/centraldashboard:v0.3.0",
};
local env = {
namespace: "kftest",
};
local centraldash = centraldashboard.new(params, env);
local testCases = [
{
actual: centraldash.centralDashboardDeployment,
expected: {
apiVersion: "extensions/v1beta1",
kind: "Deployment",
metadata: {
labels: {
app: "centraldashboard",
},
name: "centraldashboard",
namespace: "kftest",
},
spec: {
template: {
metadata: {
labels: {
app: "centraldashboard",
},
},
spec: {
containers: [
{
image: "gcr.io/kubeflow-images-public/centraldashboard:v0.3.0",
name: "centraldashboard",
ports: [
{
containerPort: 8082,
},
],
},
],
serviceAccountName: "centraldashboard",
},
},
},
},
},
{
actual: centraldash.centralDashboardService,
expected: {
local annotations = function(namespace) {
"getambassador.io/config":
std.join("\n", [
"---",
"apiVersion: ambassador/v0",
"kind: Mapping",
"name: centralui-mapping",
"prefix: /",
"rewrite: /",
"service: centraldashboard." + namespace,
]),
},
apiVersion: "v1",
kind: "Service",
metadata: {
labels: {
app: "centraldashboard",
},
name: "centraldashboard",
namespace: "kftest",
annotations: annotations("kftest"),
},
spec: {
ports: [
{
port: 80,
targetPort: 8082,
},
],
selector: {
app: "centraldashboard",
},
sessionAffinity: "None",
type: "ClusterIP",
},
},
},
{
actual: centraldash.centralDashboardServiceAccount,
expected: {
apiVersion: "v1",
kind: "ServiceAccount",
metadata: {
name: "centraldashboard",
namespace: "kftest",
},
},
},
{
actual: centraldash.centralDashboardRole,
expected: {
apiVersion: "rbac.authorization.k8s.io/v1beta1",
kind: "Role",
metadata: {
labels: {
app: "centraldashboard",
},
name: "centraldashboard",
namespace: "kftest",
},
rules: [
{
apiGroups: [""],
resources: [
"pods",
"pods/exec",
"pods/log",
],
verbs: [
"get",
"list",
"watch",
],
},
{
apiGroups: [""],
resources: [
"secrets",
],
verbs: [
"get",
],
},
],
},
},
{
actual: centraldash.centralDashboardRoleBinding,
expected: {
apiVersion: "rbac.authorization.k8s.io/v1beta1",
kind: "RoleBinding",
metadata: {
labels: {
app: "centraldashboard",
},
name: "centraldashboard",
namespace: "kftest",
},
roleRef: {
apiGroup: "rbac.authorization.k8s.io",
kind: "Role",
name: "centraldashboard",
},
subjects: [
{
kind: "ServiceAccount",
name: "centraldashboard",
namespace: "kftest",
},
],
},
},
];
testSuite.run(testCases)

View File

@ -0,0 +1,88 @@
local echoServer = import "kubeflow/common/echo-server.libsonnet";
local testSuite = import "kubeflow/common/testsuite.libsonnet";
local params = {
name: "echo-server",
image: "gcr.io/kubeflow-images-staging/echo-server:v20180628-44f08d31",
};
local env = {
namespace: "kubeflow",
};
local instance = echoServer.new(env, params);
local testCases = [
{
actual: instance.parts.service,
expected: {
apiVersion: "v1",
kind: "Service",
metadata: {
annotations: {
"getambassador.io/config": "---\napiVersion: ambassador/v0\nkind: Mapping\nname: echo-server-mapping\nprefix: /echo-server\nrewrite: /\nservice: echo-server.kubeflow",
},
labels: {
app: "echo-server",
},
name: "echo-server",
namespace: "kubeflow",
},
spec: {
ports: [
{
port: 80,
targetPort: 8080,
},
],
selector: {
app: "echo-server",
},
type: "ClusterIP",
},
},
},
{
actual: instance.parts.deployment,
expected: {
apiVersion: "extensions/v1beta1",
kind: "Deployment",
metadata: {
name: "echo-server",
namespace: "kubeflow",
},
spec: {
replicas: 1,
template: {
metadata: {
labels: {
app: "echo-server",
},
},
spec: {
containers: [
{
image: "gcr.io/kubeflow-images-staging/echo-server:v20180628-44f08d31",
name: "app",
ports: [
{
containerPort: 8080,
},
],
readinessProbe: {
httpGet: {
path: "/headers",
port: 8080,
},
initialDelaySeconds: 5,
periodSeconds: 30,
},
},
],
},
},
},
},
},
];
testSuite.run(testCases)

View File

@ -0,0 +1,122 @@
local spartakus = import "kubeflow/common/spartakus.libsonnet";
local testSuite = import "kubeflow/common/testsuite.libsonnet";
local params = {
name: "spartakus",
usageId: "unknown_cluster",
reportUsage: "false",
};
local env = {
namespace: "kubeflow",
};
local instance = spartakus.new(env, params);
local testCases = [
{
actual: instance.parts.clusterRole,
expected: {
apiVersion: "rbac.authorization.k8s.io/v1beta1",
kind: "ClusterRole",
metadata: {
labels: {
app: "spartakus",
},
name: "spartakus",
},
rules: [
{
apiGroups: [
"",
],
resources: [
"nodes",
],
verbs: [
"get",
"list",
],
},
],
},
},
{
actual: instance.parts.clusterRoleBinding,
expected: {
apiVersion: "rbac.authorization.k8s.io/v1beta1",
kind: "ClusterRoleBinding",
metadata: {
labels: {
app: "spartakus",
},
name: "spartakus",
},
roleRef: {
apiGroup: "rbac.authorization.k8s.io",
kind: "ClusterRole",
name: "spartakus",
},
subjects: [
{
kind: "ServiceAccount",
name: "spartakus",
namespace: "kubeflow",
},
],
},
},
{
actual: instance.parts.serviceAccount,
expected: {
apiVersion: "v1",
kind: "ServiceAccount",
metadata: {
labels: {
app: "spartakus",
},
name: "spartakus",
namespace: "kubeflow",
},
},
},
{
actual: instance.parts.volunteer,
expected: {
apiVersion: "extensions/v1beta1",
kind: "Deployment",
metadata: {
labels: {
app: "spartakus",
},
name: "spartakus-volunteer",
namespace: "kubeflow",
},
spec: {
replicas: 1,
template: {
metadata: {
labels: {
app: "spartakus-volunteer",
},
},
spec: {
containers: [
{
args: [
"volunteer",
"--cluster-id=unknown_cluster",
"--database=https://stats-collector.kubeflow.org",
],
image: "gcr.io/google_containers/spartakus-amd64:v1.1.0",
name: "volunteer",
},
],
serviceAccountName: "spartakus",
},
},
},
},
},
];
testSuite.run(testCases)

View File

@ -0,0 +1,56 @@
local util = import "../util.libsonnet";
std.assertEqual(util.lower("aTruez"), "atruez") &&
std.assertEqual(util.lower("ATrUez"), "atruez") &&
std.assertEqual(util.lower("atruez"), "atruez") &&
std.assertEqual(util.lower("ATRUEZ"), "atruez") &&
std.assertEqual(util.toBool(false), false) &&
std.assertEqual(util.toBool(true), true) &&
std.assertEqual(util.toBool("true"), true) &&
std.assertEqual(util.toBool("True"), true) &&
std.assertEqual(util.toBool("TRUE"), true) &&
std.assertEqual(util.toBool("false"), false) &&
std.assertEqual(util.toBool("False"), false) &&
std.assertEqual(util.toBool("FALSE"), false) &&
std.assertEqual(util.toBool("random string"), false) &&
std.assertEqual(util.toBool(1), true) &&
std.assertEqual(util.toBool(0), false) &&
std.assertEqual(util.toBool(123), true) &&
std.assertEqual(util.toArray("a,b,c,d"), ["a", "b", "c", "d"]) &&
std.assertEqual(util.toArray("ca, or,fl, mo"), ["ca", "or", "fl", "mo"]) &&
std.assertEqual(std.length(util.toArray(2)), 0) &&
std.assertEqual(std.length(util.toArray("hello world")), 1) &&
std.assertEqual(std.length(util.toArray([1, 2, 3, 4])), 0) &&
std.assertEqual(util.sort(["Craydad", "CCall", "crayon"]), ["CCall", "Craydad", "crayon"]) &&
std.assertEqual(
{
new():: self + {
local configMap = {
kind: "ConfigMap",
},
local service = {
kind: "Service",
},
list:: util.list([configMap, service]),
},
}.new().list,
{
apiVersion: "v1",
items: [
{
kind: "ConfigMap",
},
{
kind: "Service",
},
],
kind: "List",
}
) &&
std.assertEqual(
util.setDiff(
util.sort(["CCall", "Craydad", "crayon", "fuzzball"]),
util.sort(["CCall", "Craydad", "crayon"])
),
["fuzzball"]
)

View File

@ -0,0 +1,29 @@
// Some useful routines.
{
run:: function(testCases) {
local testEqual(x) = x {
pass: x.actual == x.expected,
},
local curry(testCases) = {
// For each test case determine whether expected matches equals
local testCasesWithResults = std.map(
testEqual,
testCases,
),
return::
testCasesWithResults,
}.return,
// Compute test suite.
local foldResults(left, right) = {
pass: left.pass && right.pass,
},
local initResult = { pass: true },
local suiteResult = std.foldl(foldResults, curry(testCases), initResult),
local testSuite = suiteResult {
testCases: curry(testCases),
},
result::
testSuite,
}.result,
}

View File

@ -0,0 +1,207 @@
// Some useful routines.
{
local k = import "k.libsonnet",
local util = self,
// Convert a string to lower case.
lower:: function(x) {
local cp(c) = std.codepoint(c),
local lowerLetter(c) = if cp(c) >= 65 && cp(c) < 91 then
std.char(cp(c) + 32)
else c,
result:: std.join("", std.map(lowerLetter, std.stringChars(x))),
}.result,
// Convert non-boolean types like string,number to a boolean.
// This is primarily intended for dealing with parameters that should be booleans.
toBool:: function(x) {
result::
if std.type(x) == "boolean" then
x
else if std.type(x) == "string" then
std.asciiUpper(x) == "TRUE"
else if std.type(x) == "number" then
x != 0
else
false,
}.result,
// Convert a comma-delimited string to an Array
toArray:: function(str) {
local trim(str) = {
rest::
if std.startsWith(str, " ") then
std.substr(str, 1, std.length(str) - 1)
else
str,
}.rest,
result::
if std.type(str) == "string" && str != "null" && std.length(str) > 0 then
std.map(trim, std.split(str, ","))
else [],
}.result,
foldl:: function(key, value, objs) {
local aux(arr, i, running) =
if i >= std.length(arr) then
running
else
aux(arr, i + 1, running { [key(arr[i])]+: value(arr[i]) }) tailstrict,
return:: aux(objs, 0, {},),
}.return,
sort:: function(arr, compare=function(a, b) {
return::
if a == b then
0
else
if a < b then
-1
else
1,
}.return) {
local _sort(arr, compare) = {
local l = std.length(arr),
local f = {
local pivot = arr[0],
local rest = std.makeArray(l - 1, function(i) arr[i + 1]),
local lessorequal(x) = compare(x, pivot) <= 0,
local greater(x) = compare(x, pivot) > 0,
local left = _sort(std.filter(lessorequal, rest), compare) tailstrict,
local right = _sort(std.filter(greater, rest), compare) tailstrict,
return:: left + [pivot] + right,
}.return,
return::
if l == 0 then
[]
else
f,
}.return,
return:: _sort(arr, compare),
}.return,
setDiff:: function(a, b, compare=function(a, b) {
return::
if a == b then
0
else if a < b then
-1
else
1,
}.return) {
local aux(a, b, i, j, acc) =
if i >= std.length(a) then
acc
else
if j >= std.length(b) then
aux(a, b, i + 1, j, acc + [a[i]]) tailstrict
else
if compare(a[i], b[j]) == 0 then
aux(a, b, i + 1, j + 1, acc) tailstrict
else
if compare(a[i], b[j]) == -1 then
aux(a, b, i + 1, j, acc + [a[i]]) tailstrict
else
aux(a, b, i, j + 1, acc) tailstrict,
return:: aux(a, b, 0, 0, []) tailstrict,
}.return,
getApiVersionKindAndMetadata(resource):: {
return::
if std.objectHas(resource.metadata, "resourceVersion") then {
apiVersion: resource.apiVersion,
kind: resource.kind,
metadata: {
labels: resource.metadata.labels,
name: resource.metadata.name,
namespace: resource.metadata.namespace,
resourceVersion: resource.metadata.resourceVersion,
}
} else {
apiVersion: resource.apiVersion,
kind: resource.kind,
metadata: {
labels: resource.metadata.labels,
name: resource.metadata.name,
namespace: resource.metadata.namespace,
},
},
}.return,
groupByResource(resources):: {
local getKey(resource) = {
return::
resource.kind,
}.return,
local getValue(resource) = {
return::
{ [resource.metadata.name]+: resource },
}.return,
return:: util.foldl(getKey, getValue, resources),
}.return,
comparator(a, b):: {
return::
if a.metadata.name == b.metadata.name then
0
else
if a.metadata.name < b.metadata.name then
-1
else
1,
}.return,
validateResource(resource):: {
return::
if std.type(resource) == "object" &&
std.objectHas(resource, "kind") &&
std.objectHas(resource, "apiVersion") &&
std.objectHas(resource, "metadata") &&
std.objectHas(resource.metadata, "name") then
true
else
false,
}.return,
extractGroups(obj)::
if std.type(obj) == "object" then
[obj[key] for key in std.objectFields(obj)]
else
[],
extractResources(group)::
if std.type(group) == "object" then
[group[key] for key in std.objectFields(group)]
else
[],
curryResources(resources, exists):: {
local existingResource(resource) = {
local resourceExists(kind, name) = {
return::
if std.objectHas(resources, kind) &&
std.objectHas(resources[kind], name) then
true
else
false,
}.return,
return::
if util.validateResource(resource) then
resourceExists(resource.kind, resource.metadata.name)
else
false,
}.return,
local missingResource(resource) = {
return::
existingResource(resource) == false,
}.return,
return::
if exists == true then
existingResource
else
missingResource,
}.return,
// Produce a list of manifests. obj must be an array
list(obj):: k.core.v1.list.new(obj,),
}

View File

@ -0,0 +1,8 @@
{
"Major": "0",
"Minor": "2",
"Patch": "devel",
"GitCommit": "",
"BuildDate": "",
"ksonnetVersion": "0.9.2",
}

View File

@ -0,0 +1,15 @@
{
all(params):: [
{
apiVersion: "v1",
kind: "ConfigMap",
metadata: {
name: "kubeflow-version",
namespace: params.namespace,
},
data: {
"kubeflow-version": importstr "version-info.json",
},
},
],
}

View File

@ -0,0 +1,22 @@
{
"name": "kubeflow examples",
"apiVersion": "0.0.1",
"kind": "ksonnet.io/parts",
"description": "kubeflow examples.\n",
"author": "kubeflow-team <kubeflow-discuss@googlegroups.com>",
"contributors": [
],
"repository": {
"type": "git",
"url": "https://github.com/kubeflow/kubeflow"
},
"bugs": {
"url": "https://github.com/kubeflow/kubeflow/issues"
},
"keywords": [
"kubernetes",
"kubeflow",
"machine learning"
],
"license": "Apache 2.0",
}

View File

@ -0,0 +1,88 @@
// @apiVersion 0.1
// @name io.ksonnet.pkg.katib-studyjob-test-v1alpha1
// @description katib-studyjob-test
// @shortDescription A Katib StudyJob using random suggestion
// @param name string Name for the job.
local k = import "k.libsonnet";
local name = params.name;
local namespace = env.namespace;
local studyjob = {
apiVersion: "kubeflow.org/v1alpha1",
kind: "StudyJob",
metadata: {
name: name,
namespace: namespace,
},
spec: {
studyName: name,
owner: "crd",
optimizationtype: "maximize",
objectivevaluename: "Validation-accuracy",
optimizationgoal: 0.99,
requestcount: 1,
metricsnames: ["accuracy"],
parameterconfigs: [
{
name: "--lr",
parametertype: "double",
feasible: {
min: "0.01",
max: "0.03",
},
},
{
name: "--num-layers",
parametertype: "int",
feasible: {
min: "2",
max: "5",
},
},
{
name: "--optimizer",
parametertype: "categorical",
feasible: {
list: ["sgd", "adam", "ftrl"],
},
},
],
workerSpec: {
goTemplate: {
rawTemplate: |||
apiVersion: batch/v1
kind: Job
metadata:
name: {{.WorkerID}}
namespace: {{.NameSpace}}
spec:
template:
spec:
containers:
- name: {{.WorkerID}}
image: katib/mxnet-mnist-example
command:
- "python"
- "/mxnet/example/image-classification/train_mnist.py"
- "--batch-size=64"
{{- with .HyperParameters}}
{{- range .}}
- "{{.Name}}={{.Value}}"
{{- end}}
{{- end}}
restartPolicy: Never
|||,
},
},
suggestionSpec: {
suggestionAlgorithm: "random",
requestNumber: 1,
},
},
};
k.core.v1.list.new([
studyjob,
])

View File

@ -0,0 +1,14 @@
// @apiVersion 1
// @name io.ksonnet.pkg.tensorboard
// @description Tensorboard components
// @shortDescription ksonnet components for Tensorboard
// @param name string Name to give to each of the components
// @optionalParam logDir string logs Name of the log directory holding the TF events file
// @optionalParam targetPort number 6006 Name of the targetPort
// @optionalParam servicePort number 9000 Name of the servicePort
// @optionalParam serviceType string ClusterIP The service type for tensorboard service
// @optionalParam defaultTbImage string tensorflow/tensorflow:1.8.0 default tensorboard image to use
local tensorboard = import "kubeflow/tensorboard/tensorboard.libsonnet";
local instance = tensorboard.new(env, params);
instance.list(instance.all)

View File

@ -0,0 +1,82 @@
// @apiVersion 0.1
// @name io.ksonnet.pkg.tf-job-simple-v1beta1
// @description tf-job-simple
// @shortDescription A simple TFJob to run CNN benchmark
// @param name string Name for the job.
local k = import "k.libsonnet";
local name = params.name;
local namespace = env.namespace;
local image = "gcr.io/kubeflow/tf-benchmarks-cpu:v20171202-bdab599-dirty-284af3";
local tfjob = {
apiVersion: "kubeflow.org/v1beta1",
kind: "TFJob",
metadata: {
name: name,
namespace: namespace,
},
spec: {
tfReplicaSpecs: {
Worker: {
replicas: 1,
template: {
spec: {
containers: [
{
args: [
"python",
"tf_cnn_benchmarks.py",
"--batch_size=32",
"--model=resnet50",
"--variable_update=parameter_server",
"--flush_stdout=true",
"--num_gpus=1",
"--local_parameter_device=cpu",
"--device=cpu",
"--data_format=NHWC",
],
image: image,
name: "tensorflow",
workingDir: "/opt/tf-benchmarks/scripts/tf_cnn_benchmarks",
},
],
restartPolicy: "OnFailure",
},
},
},
Ps: {
template: {
spec: {
containers: [
{
args: [
"python",
"tf_cnn_benchmarks.py",
"--batch_size=32",
"--model=resnet50",
"--variable_update=parameter_server",
"--flush_stdout=true",
"--num_gpus=1",
"--local_parameter_device=cpu",
"--device=cpu",
"--data_format=NHWC",
],
image: image,
name: "tensorflow",
workingDir: "/opt/tf-benchmarks/scripts/tf_cnn_benchmarks",
},
],
restartPolicy: "OnFailure",
},
},
tfReplicaType: "PS",
},
},
},
};
k.core.v1.list.new([
tfjob,
])

View File

@ -0,0 +1,82 @@
// @apiVersion 0.1
// @name io.ksonnet.pkg.tf-job-simple-v1beta2
// @description tf-job-simple
// @shortDescription A simple TFJob to run CNN benchmark
// @param name string Name for the job.
local k = import "k.libsonnet";
local name = params.name;
local namespace = env.namespace;
local image = "gcr.io/kubeflow/tf-benchmarks-cpu:v20171202-bdab599-dirty-284af3";
local tfjob = {
apiVersion: "kubeflow.org/v1beta2",
kind: "TFJob",
metadata: {
name: name,
namespace: namespace,
},
spec: {
tfReplicaSpecs: {
Worker: {
replicas: 1,
template: {
spec: {
containers: [
{
args: [
"python",
"tf_cnn_benchmarks.py",
"--batch_size=32",
"--model=resnet50",
"--variable_update=parameter_server",
"--flush_stdout=true",
"--num_gpus=1",
"--local_parameter_device=cpu",
"--device=cpu",
"--data_format=NHWC",
],
image: image,
name: "tensorflow",
workingDir: "/opt/tf-benchmarks/scripts/tf_cnn_benchmarks",
},
],
restartPolicy: "OnFailure",
},
},
},
Ps: {
template: {
spec: {
containers: [
{
args: [
"python",
"tf_cnn_benchmarks.py",
"--batch_size=32",
"--model=resnet50",
"--variable_update=parameter_server",
"--flush_stdout=true",
"--num_gpus=1",
"--local_parameter_device=cpu",
"--device=cpu",
"--data_format=NHWC",
],
image: image,
name: "tensorflow",
workingDir: "/opt/tf-benchmarks/scripts/tf_cnn_benchmarks",
},
],
restartPolicy: "OnFailure",
},
},
tfReplicaType: "PS",
},
},
},
};
k.core.v1.list.new([
tfjob,
])

View File

@ -0,0 +1,82 @@
// @apiVersion 0.1
// @name io.ksonnet.pkg.tf-job-simple
// @description tf-job-simple
// @shortDescription A simple TFJob to run CNN benchmark
// @param name string Name for the job.
local k = import "k.libsonnet";
local name = params.name;
local namespace = env.namespace;
local image = "gcr.io/kubeflow/tf-benchmarks-cpu:v20171202-bdab599-dirty-284af3";
local tfjob = {
apiVersion: "kubeflow.org/v1alpha2",
kind: "TFJob",
metadata: {
name: name,
namespace: namespace,
},
spec: {
tfReplicaSpecs: {
Worker: {
replicas: 1,
template: {
spec: {
containers: [
{
args: [
"python",
"tf_cnn_benchmarks.py",
"--batch_size=32",
"--model=resnet50",
"--variable_update=parameter_server",
"--flush_stdout=true",
"--num_gpus=1",
"--local_parameter_device=cpu",
"--device=cpu",
"--data_format=NHWC",
],
image: image,
name: "tensorflow",
workingDir: "/opt/tf-benchmarks/scripts/tf_cnn_benchmarks",
},
],
restartPolicy: "OnFailure",
},
},
},
Ps: {
template: {
spec: {
containers: [
{
args: [
"python",
"tf_cnn_benchmarks.py",
"--batch_size=32",
"--model=resnet50",
"--variable_update=parameter_server",
"--flush_stdout=true",
"--num_gpus=1",
"--local_parameter_device=cpu",
"--device=cpu",
"--data_format=NHWC",
],
image: image,
name: "tensorflow",
workingDir: "/opt/tf-benchmarks/scripts/tf_cnn_benchmarks",
},
],
restartPolicy: "OnFailure",
},
},
tfReplicaType: "PS",
},
},
},
};
k.core.v1.list.new([
tfjob,
])

View File

@ -0,0 +1,94 @@
// @apiVersion 0.1
// @name io.ksonnet.pkg.tf-serving-simple
// @description tf-serving-simple
// @shortDescription tf-serving-simple
// @param name string Name to give to each of the components
local k = import "k.libsonnet";
local namespace = "default";
local appName = import "param://name";
local modelBasePath = "gs://kubeflow-models/inception";
local modelName = "inception";
local image = "gcr.io/kubeflow-images-public/tf-model-server-cpu:v20180327-995786ec";
local service = {
apiVersion: "v1",
kind: "Service",
metadata: {
labels: {
app: appName,
},
name: appName,
namespace: namespace,
},
spec: {
ports: [
{
name: "grpc-tf-serving",
port: 9000,
targetPort: 9000,
},
],
selector: {
app: appName,
},
type: "ClusterIP",
},
};
local deployment = {
apiVersion: "extensions/v1beta1",
kind: "Deployment",
metadata: {
labels: {
app: appName,
},
name: appName,
namespace: namespace,
},
spec: {
template: {
metadata: {
labels: {
app: appName,
},
},
spec: {
containers: [
{
args: [
"/usr/bin/tensorflow_model_server",
"--port=9000",
"--model_name=" + modelName,
"--model_base_path=" + modelBasePath,
],
image: image,
imagePullPolicy: "IfNotPresent",
name: "inception",
ports: [
{
containerPort: 9000,
},
],
resources: {
limits: {
cpu: "4",
memory: "4Gi",
},
requests: {
cpu: "1",
memory: "1Gi",
},
},
},
],
},
},
},
};
k.core.v1.list.new([
service,
deployment,
])

View File

@ -0,0 +1,179 @@
// @apiVersion 0.1
// @name io.ksonnet.pkg.tf-serving-with-istio
// @description tf-serving-with-istio
// @shortDescription tf-serving-with-istio
// @param name string Name to give to each of the components
local k = import "k.libsonnet";
local namespace = "default";
local appName = import "param://name";
local modelBasePath = "gs://kubeflow-models/inception";
local modelName = "inception";
local image = "gcr.io/kubeflow-images-public/tf-model-server-cpu:v20180327-995786ec";
local httpProxyImage = "gcr.io/kubeflow-images-public/tf-model-server-http-proxy:v20180327-995786ec";
local routeRule = {
apiVersion: "config.istio.io/v1alpha2",
kind: "RouteRule",
metadata: {
name: appName,
namespace: namespace,
},
spec: {
destination: {
name: "tf-serving",
},
precedence: 0,
route: [
{
labels: {
version: "v1",
},
},
],
},
};
local service = {
apiVersion: "v1",
kind: "Service",
metadata: {
annotations: {
"getambassador.io/config":
std.join("\n", [
"---",
"apiVersion: ambassador/v0",
"kind: Mapping",
"name: tfserving-mapping-tf-serving-get",
"prefix: /models/tf-serving/",
"rewrite: /",
"method: GET",
"service: tf-serving." + namespace + ":8000",
"---",
"apiVersion: ambassador/v0",
"kind: Mapping",
"name: tfserving-mapping-tf-serving-post",
"prefix: /models/tf-serving/",
"rewrite: /model/tf-serving:predict",
"method: POST",
"service: tf-serving." + namespace + ":8000",
]),
},
labels: {
app: appName,
},
name: appName,
namespace: namespace,
},
spec: {
ports: [
{
name: "grpc-tf-serving",
port: 9000,
targetPort: 9000,
},
{
name: "http-tf-serving-proxy",
port: 8000,
targetPort: 8000,
},
],
selector: {
app: appName,
},
type: "ClusterIP",
},
};
local deployment = {
apiVersion: "extensions/v1beta1",
kind: "Deployment",
metadata: {
labels: {
app: appName,
},
name: appName,
namespace: namespace,
},
spec: {
template: {
metadata: {
labels: {
app: appName,
},
annotations: {
"sidecar.istio.io/inject": "true",
},
},
spec: {
containers: [
{
args: [
"/usr/bin/tensorflow_model_server",
"--port=9000",
"--model_name=" + modelName,
"--model_base_path=" + modelBasePath,
],
image: image,
imagePullPolicy: "IfNotPresent",
name: "inception",
ports: [
{
containerPort: 9000,
},
],
resources: {
limits: {
cpu: "4",
memory: "4Gi",
},
requests: {
cpu: "1",
memory: "1Gi",
},
},
},
{
name: appName + "-http-proxy",
image: httpProxyImage,
imagePullPolicy: "IfNotPresent",
command: [
"python",
"/usr/src/app/server.py",
"--port=8000",
"--rpc_port=9000",
"--rpc_timeout=10.0",
],
env: [],
ports: [
{
containerPort: 8000,
},
],
resources: {
requests: {
memory: "1Gi",
cpu: "1",
},
limits: {
memory: "4Gi",
cpu: "4",
},
},
securityContext: {
runAsUser: 1000,
fsGroup: 1000,
},
},
],
},
},
},
};
k.core.v1.list.new([
routeRule,
service,
deployment,
])

View File

@ -0,0 +1,102 @@
local tensorboard = import "kubeflow/tensorboard/tensorboard.libsonnet";
local params = {
name: "tensorboard",
logDir: "logs",
targetPort: "6006",
servicePort: "9000",
serviceType: "LoadBalancer",
defaultTbImage: "tensorflow/tensorflow:1.9.0",
};
local env = {
namespace: "test-kf-001",
};
local instance = tensorboard.new(env, params);
std.assertEqual(
instance.tbService,
{
apiVersion: "v1",
kind: "Service",
metadata: {
annotations: {
"getambassador.io/config": "---\napiVersion: ambassador/v0\nkind: Mapping\nname: tb-mapping-tensorboard-get\nprefix: /tensorboard/ tensorboard/\nrewrite: /\nmethod: GET\nservice: tensorboard.test-kf-001:9000",
},
labels: {
app: "tensorboard",
},
name: "tensorboard",
namespace: "test-kf-001",
},
spec: {
ports: [
{
name: "tb",
port: "9000",
targetPort: "6006",
},
],
selector: {
app: "tensorboard",
},
type: "LoadBalancer",
},
}
) &&
std.assertEqual(
instance.tbDeployment,
{
apiVersion: "apps/v1beta1",
kind: "Deployment",
metadata: {
labels: {
app: "tensorboard",
},
name: "tensorboard",
namespace: "test-kf-001",
},
spec: {
replicas: 1,
template: {
metadata: {
labels: {
app: "tensorboard",
},
},
spec: {
containers: [
{
args: [
"--logdir=logs",
"--port=6006",
],
command: [
"/usr/local/bin/tensorboard",
],
image: "tensorflow/tensorflow:1.9.0",
imagePullPolicy: "IfNotPresent",
name: "tensorboard",
ports: [
{
containerPort: "6006",
},
],
resources: {
limits: {
cpu: "4",
memory: "4Gi",
},
requests: {
cpu: "1",
memory: "1Gi",
},
},
},
],
},
},
},
}
)

View File

@ -0,0 +1,7 @@
approvers:
- abhi-g
- jlewi
- kunmingg
- lluunn
- r2d4
- richardsliu

View File

@ -0,0 +1,12 @@
# gcp
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [gcp](#gcp)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
> This ksonnet package contains GCP specific prototypes.

View File

@ -0,0 +1,451 @@
{
local k = import "k.libsonnet",
local util = import "kubeflow/common/util.libsonnet",
new(_env, _params):: {
local params = _params + _env {
hostname: if std.objectHas(_params, "hostname") then _params.hostname else "null",
ingressName: "envoy-ingress"
},
local namespace = params.namespace,
// Test if the given hostname is in the form of: "NAME.endpoints.PROJECT.cloud.goog"
local isCloudEndpoint(str) = {
local toks = if std.type(str) == "null" then [] else std.split(str, "."),
result::
(std.length(toks) == 5 && toks[1] == "endpoints" && toks[3] == "cloud" && toks[4] == "goog"),
}.result,
local initServiceAccount = {
apiVersion: "v1",
kind: "ServiceAccount",
metadata: {
name: "envoy",
namespace: namespace,
},
}, // initServiceAccount
initServiceAccount:: initServiceAccount,
local initClusterRoleBinding = {
kind: "ClusterRoleBinding",
apiVersion: "rbac.authorization.k8s.io/v1beta1",
metadata: {
name: "envoy",
},
subjects: [
{
kind: "ServiceAccount",
name: "envoy",
namespace: namespace,
},
],
roleRef: {
kind: "ClusterRole",
name: "envoy",
apiGroup: "rbac.authorization.k8s.io",
},
}, // initClusterRoleBinding
initClusterRoleBinding:: initClusterRoleBinding,
local initClusterRole = {
kind: "ClusterRole",
apiVersion: "rbac.authorization.k8s.io/v1beta1",
metadata: {
name: "envoy",
namespace: namespace,
},
rules: [
{
apiGroups: [""],
resources: ["services", "configmaps", "secrets"],
verbs: ["get", "list", "patch", "update"],
},
{
apiGroups: ["extensions"],
resources: ["ingresses"],
verbs: ["get", "list", "update", "patch"],
},
],
}, // initClusterRoleBinding
initClusterRole:: initClusterRole,
local configMap = {
apiVersion: "v1",
kind: "ConfigMap",
metadata: {
name: "envoy-config",
namespace: namespace,
},
data: {
"update_backend.sh": importstr "update_backend.sh",
},
},
configMap:: configMap,
local whoamiService = {
apiVersion: "v1",
kind: "Service",
metadata: {
labels: {
app: "whoami",
},
name: "whoami-app",
namespace: params.namespace,
annotations: {
"getambassador.io/config":
std.join("\n", [
"---",
"apiVersion: ambassador/v0",
"kind: Mapping",
"name: whoami-mapping",
"prefix: /whoami",
"rewrite: /whoami",
"service: whoami-app." + namespace,
]),
}, //annotations
},
spec: {
ports: [
{
port: 80,
targetPort: 8081,
},
],
selector: {
app: "whoami",
},
type: "ClusterIP",
},
}, // whoamiService
whoamiService:: whoamiService,
local whoamiApp = {
apiVersion: "extensions/v1beta1",
kind: "Deployment",
metadata: {
name: "whoami-app",
namespace: params.namespace,
},
spec: {
replicas: 1,
template: {
metadata: {
labels: {
app: "whoami",
},
},
spec: {
containers: [
{
env: [
{
name: "PORT",
value: "8081",
},
],
image: "gcr.io/cloud-solutions-group/esp-sample-app:1.0.0",
name: "app",
ports: [
{
containerPort: 8081,
},
],
readinessProbe: {
failureThreshold: 2,
httpGet: {
path: "/healthz",
port: 8081,
scheme: "HTTP",
},
periodSeconds: 10,
successThreshold: 1,
timeoutSeconds: 5,
},
},
],
},
},
},
},
whoamiApp:: whoamiApp,
// Run the process to update the backend service
local backendUpdater = {
apiVersion: "apps/v1",
kind: "StatefulSet",
metadata: {
name: "backend-updater",
namespace: namespace,
labels: {
service: "backend-updater",
},
},
spec: {
selector: {
matchLabels: {
service: "backend-updater",
},
},
template: {
metadata: {
labels: {
service: "backend-updater",
},
},
spec: {
serviceAccountName: "envoy",
containers: [
{
name: "backend-updater",
image: params.ingressSetupImage,
command: [
"bash",
"/var/envoy-config/update_backend.sh",
],
env: [
{
name: "NAMESPACE",
value: namespace,
},
{
name: "SERVICE",
value: "ambassador",
},
{
name: "GOOGLE_APPLICATION_CREDENTIALS",
value: "/var/run/secrets/sa/admin-gcp-sa.json",
},
{
name: "HEALTHCHECK_PATH",
value: "/whoami",
},
{
name: "INGRESS_NAME",
value: params.ingressName,
},
],
volumeMounts: [
{
mountPath: "/var/envoy-config/",
name: "config-volume",
},
{
name: "sa-key",
readOnly: true,
mountPath: "/var/run/secrets/sa",
},
],
},
],
volumes: [
{
configMap: {
name: "envoy-config",
},
name: "config-volume",
},
{
name: "sa-key",
secret: {
secretName: "admin-gcp-sa",
},
},
],
},
},
},
}, // backendUpdater
backendUpdater:: backendUpdater,
// TODO(danisla): Remove afer https://github.com/kubernetes/ingress-gce/pull/388 is resolved per #1327.
local ingressBootstrapConfigMap = {
apiVersion: "v1",
kind: "ConfigMap",
metadata: {
name: "ingress-bootstrap-config",
namespace: namespace,
},
data: {
"ingress_bootstrap.sh": importstr "ingress_bootstrap.sh",
},
},
ingressBootstrapConfigMap:: ingressBootstrapConfigMap,
local ingressBootstrapJob = {
apiVersion: "batch/v1",
kind: "Job",
metadata: {
name: "ingress-bootstrap",
namespace: namespace,
},
spec: {
template: {
spec: {
restartPolicy: "OnFailure",
serviceAccountName: "envoy",
containers: [
{
name: "bootstrap",
image: params.ingressSetupImage,
command: ["/var/ingress-config/ingress_bootstrap.sh"],
env: [
{
name: "NAMESPACE",
value: namespace,
},
{
name: "TLS_SECRET_NAME",
value: params.secretName,
},
{
name: "TLS_HOST_NAME",
value: params.hostname,
},
{
name: "INGRESS_NAME",
value: "envoy-ingress",
},
],
volumeMounts: [
{
mountPath: "/var/ingress-config/",
name: "ingress-config",
},
],
},
],
volumes: [
{
configMap: {
name: "ingress-bootstrap-config",
// TODO(danisla): replace with std.parseOctal("0755") after upgrading to ksonnet 0.12
defaultMode: 493,
},
name: "ingress-config",
},
],
},
},
},
}, // ingressBootstrapJob
ingressBootstrapJob:: ingressBootstrapJob,
local ingress = {
apiVersion: "extensions/v1beta1",
kind: "Ingress",
metadata: {
name: "envoy-ingress",
namespace: namespace,
annotations: {
"kubernetes.io/tls-acme": "true",
"ingress.kubernetes.io/ssl-redirect": "true",
"kubernetes.io/ingress.global-static-ip-name": params.ipName,
"certmanager.k8s.io/issuer": params.issuer,
},
},
spec: {
rules: [
{
[if params.hostname != "null" then "host"]: params.hostname,
http: {
paths: [
{
backend: {
// Due to https://github.com/kubernetes/contrib/blob/master/ingress/controllers/gce/examples/health_checks/README.md#limitations
// Keep port the servicePort the same as the port we are targeting on the backend so that servicePort will be the same as targetPort for the purpose of
// health checking.
serviceName: "ambassador",
servicePort: 80,
},
path: "/*",
},
],
},
},
],
},
}, // iapIngress
ingress:: ingress,
local certificate = if params.privateGKECluster == "false" then (
{
apiVersion: "certmanager.k8s.io/v1alpha1",
kind: "Certificate",
metadata: {
name: params.secretName,
namespace: namespace,
},
spec: {
secretName: params.secretName,
issuerRef: {
name: params.issuer,
kind: "ClusterIssuer",
},
commonName: params.hostname,
dnsNames: [
params.hostname,
],
acme: {
config: [
{
http01: {
ingress: "envoy-ingress",
},
domains: [
params.hostname,
],
},
],
},
},
} // certificate
),
certificate:: certificate,
local cloudEndpoint = if isCloudEndpoint(params.hostname) then (
{
local makeEndpointParams(str) = {
local toks = std.split(str, "."),
result:: {
name: toks[0],
project: toks[2],
},
}.result,
local endpointParams = makeEndpointParams(params.hostname),
apiVersion: "ctl.isla.solutions/v1",
kind: "CloudEndpoint",
metadata: {
name: endpointParams.name,
namespace: namespace,
},
spec: {
project: endpointParams.project,
targetIngress: {
name: "envoy-ingress",
namespace: namespace,
},
},
} // cloudEndpoint
),
cloudEndpoint:: cloudEndpoint,
parts:: self,
all:: [
self.initServiceAccount,
self.initClusterRoleBinding,
self.initClusterRole,
self.whoamiService,
self.whoamiApp,
self.backendUpdater,
self.configMap,
self.ingressBootstrapConfigMap,
self.ingressBootstrapJob,
self.ingress,
self.certificate,
self.cloudEndpoint,
],
list(obj=self.all):: k.core.v1.list.new(obj,),
},
}

View File

@ -0,0 +1,189 @@
{
local k = import "k.libsonnet",
new(_env, _params):: {
local params = _params + _env,
local certificateCRD = {
apiVersion: "apiextensions.k8s.io/v1beta1",
kind: "CustomResourceDefinition",
metadata: {
name: "certificates.certmanager.k8s.io",
},
spec: {
group: "certmanager.k8s.io",
version: "v1alpha1",
names: {
kind: "Certificate",
plural: "certificates",
},
scope: "Namespaced",
},
},
certificateCRD:: certificateCRD,
local clusterIssuerCRD = {
apiVersion: "apiextensions.k8s.io/v1beta1",
kind: "CustomResourceDefinition",
metadata: {
name: "clusterissuers.certmanager.k8s.io",
},
spec: {
group: "certmanager.k8s.io",
version: "v1alpha1",
names: {
kind: "ClusterIssuer",
plural: "clusterissuers",
},
scope: "Cluster",
},
},
clusterIssuerCRD:: clusterIssuerCRD,
local issuerCRD = {
apiVersion: "apiextensions.k8s.io/v1beta1",
kind: "CustomResourceDefinition",
metadata: {
name: "issuers.certmanager.k8s.io",
},
spec: {
group: "certmanager.k8s.io",
version: "v1alpha1",
names: {
kind: "Issuer",
plural: "issuers",
},
scope: "Namespaced",
},
},
issuerCRD:: issuerCRD,
local serviceAccount = {
apiVersion: "v1",
kind: "ServiceAccount",
metadata: {
name: "cert-manager",
namespace: params.namespace,
},
},
serviceAccount:: serviceAccount,
local clusterRole = {
apiVersion: "rbac.authorization.k8s.io/v1beta1",
kind: "ClusterRole",
metadata: {
name: "cert-manager",
},
rules: [
{
apiGroups: ["certmanager.k8s.io"],
resources: ["certificates", "issuers", "clusterissuers"],
verbs: ["*"],
},
{
apiGroups: [""],
resources: ["secrets", "events", "endpoints", "services", "pods", "configmaps"],
verbs: ["*"],
},
{
apiGroups: ["extensions"],
resources: ["ingresses"],
verbs: ["*"],
},
],
},
clusterRole:: clusterRole,
local clusterRoleBinding = {
apiVersion: "rbac.authorization.k8s.io/v1beta1",
kind: "ClusterRoleBinding",
metadata: {
name: "cert-manager",
},
roleRef: {
apiGroup: "rbac.authorization.k8s.io",
kind: "ClusterRole",
name: "cert-manager",
},
subjects: [
{
name: "cert-manager",
namespace: params.namespace,
kind: "ServiceAccount",
},
],
},
clusterRoleBinding:: clusterRoleBinding,
local deploy = {
apiVersion: "apps/v1beta1",
kind: "Deployment",
metadata: {
name: "cert-manager",
namespace: params.namespace,
labels: {
app: "cert-manager",
},
},
spec: {
replicas: 1,
template: {
metadata: {
labels: {
app: "cert-manager",
},
},
spec: {
serviceAccountName: "cert-manager",
containers: [
{
name: "cert-manager",
image: params.certManagerImage,
imagePullPolicy: "IfNotPresent",
args: [
"--cluster-resource-namespace=" + params.namespace,
"--leader-election-namespace=" + params.namespace,
],
},
],
},
},
},
},
deploy:: deploy,
local issuerLEProd = {
apiVersion: "certmanager.k8s.io/v1alpha1",
kind: "ClusterIssuer",
metadata: {
name: "letsencrypt-prod",
},
spec: {
acme: {
server: params.acmeUrl,
email: params.acmeEmail,
privateKeySecretRef: {
name: "letsencrypt-prod-secret",
},
http01: {
},
},
},
},
issuerLEProd:: issuerLEProd,
parts:: self,
all:: [
self.certificateCRD,
self.clusterIssuerCRD,
self.issuerCRD,
self.serviceAccount,
self.clusterRole,
self.clusterRoleBinding,
self.deploy,
self.issuerLEProd,
],
list(obj=self.all):: k.core.v1.list.new(obj,),
},
}

View File

@ -0,0 +1,214 @@
{
local k = import "k.libsonnet",
new(_env, _params):: {
local params = {
cloudEndpointsImage: "gcr.io/cloud-solutions-group/cloud-endpoints-controller:0.2.1",
} + _params + _env,
local endpointsCRD = {
apiVersion: "apiextensions.k8s.io/v1beta1",
kind: "CustomResourceDefinition",
metadata: {
name: "cloudendpoints.ctl.isla.solutions",
},
spec: {
group: "ctl.isla.solutions",
version: "v1",
scope: "Namespaced",
names: {
plural: "cloudendpoints",
singular: "cloudendpoint",
kind: "CloudEndpoint",
shortNames: [
"cloudep",
"ce",
],
},
},
}, // endpointsCRD
endpointsCRD:: endpointsCRD,
local endpointsClusterRole = {
kind: "ClusterRole",
apiVersion: "rbac.authorization.k8s.io/v1beta1",
metadata: {
name: "cloud-endpoints-controller",
},
rules: [
{
apiGroups: [""],
resources: ["services", "configmaps"],
verbs: ["get", "list"],
},
{
apiGroups: ["extensions"],
resources: ["ingresses"],
verbs: ["get", "list"],
},
],
},
endpointsClusterRole:: endpointsClusterRole,
local endpointsClusterRoleBinding = {
kind: "ClusterRoleBinding",
apiVersion: "rbac.authorization.k8s.io/v1beta1",
metadata: {
name: "cloud-endpoints-controller",
},
subjects: [
{
kind: "ServiceAccount",
name: "cloud-endpoints-controller",
namespace: params.namespace,
},
],
roleRef: {
kind: "ClusterRole",
name: "cloud-endpoints-controller",
apiGroup: "rbac.authorization.k8s.io",
},
},
endpointsClusterRoleBinding:: endpointsClusterRoleBinding,
local endpointsService = {
apiVersion: "v1",
kind: "Service",
metadata: {
name: "cloud-endpoints-controller",
namespace: params.namespace,
},
spec: {
type: "ClusterIP",
ports: [
{
name: "http",
port: 80,
},
],
selector: {
app: "cloud-endpoints-controller",
},
},
}, // endpointsService
endpointsService:: endpointsService,
local endpointsServiceAccount = {
apiVersion: "v1",
kind: "ServiceAccount",
metadata: {
name: "cloud-endpoints-controller",
namespace: params.namespace,
},
}, // endpointsServiceAccount
endpointsServiceAccount:: endpointsServiceAccount,
local endpointsDeploy = {
apiVersion: "apps/v1beta1",
kind: "Deployment",
metadata: {
name: "cloud-endpoints-controller",
namespace: params.namespace,
},
spec: {
replicas: 1,
template: {
metadata: {
labels: {
app: "cloud-endpoints-controller",
},
},
spec: {
serviceAccountName: "cloud-endpoints-controller",
terminationGracePeriodSeconds: 5,
containers: [
{
name: "cloud-endpoints-controller",
image: params.cloudEndpointsImage,
imagePullPolicy: "Always",
env: [
{
name: "GOOGLE_APPLICATION_CREDENTIALS",
value: "/var/run/secrets/sa/" + params.secretKey,
},
],
volumeMounts: [
{
name: "sa-key",
readOnly: true,
mountPath: "/var/run/secrets/sa",
},
],
readinessProbe: {
httpGet: {
path: "/healthz",
port: 80,
scheme: "HTTP",
},
periodSeconds: 5,
timeoutSeconds: 5,
successThreshold: 1,
failureThreshold: 2,
},
},
],
volumes: [
{
name: "sa-key",
secret: {
secretName: params.secretName,
},
},
],
},
},
},
}, // endpointsDeploy
endpointsDeploy:: endpointsDeploy,
local endpointsCompositeController = {
apiVersion: "metacontroller.k8s.io/v1alpha1",
kind: "CompositeController",
metadata: {
name: "cloud-endpoints-controller",
},
spec: {
generateSelector: true,
resyncPeriodSeconds: 2,
parentResource: {
apiVersion: "ctl.isla.solutions/v1",
resource: "cloudendpoints",
},
childResources: [],
clientConfig: {
service: {
name: "cloud-endpoints-controller",
namespace: params.namespace,
caBundle: "...",
},
},
hooks: {
sync: {
webhook: {
url: "http://cloud-endpoints-controller." + params.namespace + "/sync",
},
},
},
},
}, // endpointsCompositeController
endpointsCompositeController:: endpointsCompositeController,
parts:: self,
local all = [
self.endpointsCRD,
self.endpointsClusterRole,
self.endpointsClusterRoleBinding,
self.endpointsService,
self.endpointsServiceAccount,
self.endpointsDeploy,
self.endpointsCompositeController,
],
all:: all,
list(obj=self.all):: k.core.v1.list.new(obj,),
},
}

View File

@ -0,0 +1,71 @@
#!/usr/bin/env bash
#
# A script to modify envoy config to perform JWT validation
# given the information for the service.
# Script executed by the iap container to configure IAP. When finished, the envoy config is created with the JWT audience.
[ -z ${NAMESPACE} ] && echo Error NAMESPACE must be set && exit 1
[ -z ${SERVICE} ] && echo Error SERVICE must be set && exit 1
PROJECT=$(curl -s -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/project/project-id)
if [ -z ${PROJECT} ]; then
echo Error unable to fetch PROJECT from compute metadata
exit 1
fi
PROJECT_NUM=$(curl -s -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/project/numeric-project-id)
if [ -z ${PROJECT_NUM} ]; then
echo Error unable to fetch PROJECT_NUM from compute metadata
exit 1
fi
checkIAP() {
# created by init container.
. /var/shared/healthz.env
# If node port or backend id change, so does the JWT audience.
CURR_NODE_PORT=$(kubectl --namespace=${NAMESPACE} get svc ${SERVICE} -o jsonpath='{.spec.ports[0].nodePort}')
CURR_BACKEND_ID=$(gcloud compute --project=${PROJECT} backend-services list --filter=name~k8s-be-${CURR_NODE_PORT}- --format='value(id)')
[ "$BACKEND_ID" == "$CURR_BACKEND_ID" ]
}
# Activate the service account
for i in $(seq 1 10); do
gcloud auth activate-service-account --key-file=${GOOGLE_APPLICATION_CREDENTIALS} && break || sleep 10
done
# Print out the config for debugging
gcloud config list
NODE_PORT=$(kubectl --namespace=${NAMESPACE} get svc ${SERVICE} -o jsonpath='{.spec.ports[0].nodePort}')
while [[ -z ${BACKEND_ID} ]]; do
BACKEND_ID=$(gcloud compute --project=${PROJECT} backend-services list --filter=name~k8s-be-${NODE_PORT}- --format='value(id)')
echo "Waiting for backend id PROJECT=${PROJECT} NAMESPACE=${NAMESPACE} SERVICE=${SERVICE} filter=name~k8s-be-${NODE_PORT}-..."
sleep 2
done
echo BACKEND_ID=${BACKEND_ID}
NODE_PORT=$(kubectl --namespace=${NAMESPACE} get svc ${SERVICE} -o jsonpath='{.spec.ports[0].nodePort}')
BACKEND_SERVICE=$(gcloud --project=${PROJECT} compute backend-services list --filter=name~k8s-be-${NODE_PORT}- --uri)
JWT_AUDIENCE="/projects/${PROJECT_NUM}/global/backendServices/${BACKEND_ID}"
# For healthcheck compare.
echo "JWT_AUDIENCE=${JWT_AUDIENCE}" > /var/shared/healthz.env
echo "NODE_PORT=${NODE_PORT}" >> /var/shared/healthz.env
echo "BACKEND_ID=${BACKEND_ID}" >> /var/shared/healthz.env
kubectl get configmap -n ${NAMESPACE} envoy-config -o jsonpath='{.data.envoy-config\.json}' |
sed -e "s|{{JWT_AUDIENCE}}|${JWT_AUDIENCE}|g" >/var/shared/envoy-config.json
echo "Restarting envoy"
curl -s ${ENVOY_ADMIN}/quitquitquit
# Verify IAP every 10 seconds.
while true; do
if ! checkIAP; then
echo "$(date) WARN: IAP check failed, restarting container."
exit 1
fi
sleep 10
done

View File

@ -0,0 +1,115 @@
#!/bin/bash
set -e
usage() {
cat <<EOF
Generate certificate suitable for use with an sidecar-injector webhook service.
This script uses k8s' CertificateSigningRequest API to a generate a
certificate signed by k8s CA suitable for use with sidecar-injector webhook
services. This requires permissions to create and approve CSR. See
https://kubernetes.io/docs/tasks/tls/managing-tls-in-a-cluster for
detailed explantion and additional instructions.
The server key/cert k8s CA cert are stored in a k8s secret.
usage: ${0} [OPTIONS]
The following flags are required.
--service Service name of webhook.
--namespace Namespace where webhook service and secret reside.
--secret Secret name for CA certificate and server certificate/key pair.
EOF
exit 1
}
while [[ $# -gt 0 ]]; do
case ${1} in
--service)
service="$2"
shift
'
--secret)
secret="$2"
shift
'
--namespace)
namespace="$2"
shift
'
*)
usage
'
esac
shift
done
[ -z ${service} ] && service=gcp-cred-webhook
[ -z ${secret} ] && secret=gcp-cred-webhook-certs
[ -z ${namespace} ] && namespace=${NAMESPACE}
[ -z ${namespace} ] && namespace=default
echo ${service}
echo ${namespace}
echo ${secret}
if [ ! -x "$(command -v openssl)" ]; then
echo "openssl not found"
exit 1
fi
csrName=${service}.${namespace}
tmpdir=$(mktemp -d)
echo "creating certs in tmpdir ${tmpdir} "
# x509 outputs a self signed certificate instead of certificate request, later used as self signed root CA
openssl req -x509 -newkey rsa:2048 -keyout ${tmpdir}/self_ca.key -out ${tmpdir}/self_ca.crt -days 365 -nodes -subj /C=/ST=/L=/O=/OU=/CN=test-certificate-authority
cat <<EOF >> ${tmpdir}/csr.conf
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = ${service}
DNS.2 = ${service}.${namespace}
DNS.3 = ${service}.${namespace}.svc
EOF
openssl genrsa -out ${tmpdir}/server-key.pem 2048
openssl req -new -key ${tmpdir}/server-key.pem -subj "/CN=${service}.${namespace}.svc" -out ${tmpdir}/server.csr -config ${tmpdir}/csr.conf
# Self sign
openssl x509 -req -days 365 -in ${tmpdir}/server.csr -CA ${tmpdir}/self_ca.crt -CAkey ${tmpdir}/self_ca.key -CAcreateserial -out ${tmpdir}/server-cert.pem
# create the secret with CA cert and server cert/key
kubectl create secret generic ${secret} \
--from-file=key.pem=${tmpdir}/server-key.pem \
--from-file=cert.pem=${tmpdir}/server-cert.pem \
--dry-run -o yaml |
kubectl -n ${namespace} apply -f -
cat ${tmpdir}/self_ca.crt
# -a means base64 encode
caBundle=`cat ${tmpdir}/self_ca.crt | openssl enc -a -A`
echo ${caBundle}
patchString='[{"op": "replace", "path": "/webhooks/0/clientConfig/caBundle", "value":"{{CA_BUNDLE}}"}]'
patchString=`echo ${patchString} | sed "s|{{CA_BUNDLE}}|${caBundle}|g"`
echo ${patchString}
checkWebhookConfig() {
currentBundle=$(kubectl get mutatingwebhookconfigurations -n ${namespace} gcp-cred-webhook -o jsonpath='{.webhooks[0].clientConfig.caBundle}')
[[ "$currentBundle" == "$caBundle" ]]
}
while true; do
if ! checkWebhookConfig; then
echo "patching ca bundle for webhook configuration..."
kubectl patch mutatingwebhookconfiguration gcp-cred-webhook \
--type='json' -p="${patchString}"
fi
sleep 10
done

View File

@ -0,0 +1,104 @@
{
local k = import "k.libsonnet",
new(_env, _params):: {
local params = _params + _env,
local persistentVolume = {
apiVersion: "v1",
kind: "PersistentVolume",
metadata: {
name: params.name,
namespace: params.namespace,
},
spec: {
capacity: {
storage: params.storageCapacity,
},
accessModes: [
"ReadWriteMany",
],
nfs: {
path: params.path,
server: params.serverIP,
},
},
},
persistentVolume:: persistentVolume,
local persistentVolumeClaim = {
apiVersion: "v1",
kind: "PersistentVolumeClaim",
metadata: {
name: params.name,
namespace: params.namespace,
},
spec: {
accessModes: [
"ReadWriteMany",
],
storageClassName: "nfs-storage",
volumeName: params.name,
resources: {
requests: {
storage: params.storageCapacity,
},
},
},
},
persistentVolumeClaim:: persistentVolumeClaim,
// Set 777 permissions on the GCFS NFS so that non-root users
// like jovyan can use that NFS share
local gcfsPersmissions = {
apiVersion: "batch/v1",
kind: "Job",
metadata: {
name: "set-gcfs-permissions",
namespace: params.namespace,
},
spec: {
template: {
spec: {
containers: [
{
name: "set-gcfs-permissions",
image: params.image,
command: [
"chmod",
"777",
"/kubeflow-gcfs",
],
volumeMounts: [
{
mountPath: "/kubeflow-gcfs",
name: params.name,
},
],
},
],
restartPolicy: "OnFailure",
volumes: [
{
name: params.name,
persistentVolumeClaim: {
claimName: params.name,
readOnly: false,
},
},
],
},
},
},
},
gcfsPersmissions:: gcfsPersmissions,
parts:: self,
all:: [
self.persistentVolume,
self.persistentVolumeClaim,
self.gcfsPersmissions,
],
list(obj=self.all):: k.core.v1.list.new(obj,),
},
}

View File

@ -0,0 +1,130 @@
{
local k = import "k.libsonnet",
new(_env, _params):: {
local params = _params + _env,
local daemonset = {
"apiVersion": "extensions/v1beta1",
"kind": "DaemonSet",
"metadata": {
"name": "nvidia-driver-installer",
"namespace": "kube-system",
"labels": {
"k8s-app": "nvidia-driver-installer"
}
},
"spec": {
"template": {
"metadata": {
"labels": {
"name": "nvidia-driver-installer",
"k8s-app": "nvidia-driver-installer"
}
},
"spec": {
"affinity": {
"nodeAffinity": {
"requiredDuringSchedulingIgnoredDuringExecution": {
"nodeSelectorTerms": [
{
"matchExpressions": [
{
"key": "cloud.google.com/gke-accelerator",
"operator": "Exists"
}
]
}
]
}
}
},
"tolerations": [
{
"operator": "Exists"
}
],
"hostNetwork": true,
"hostPID": true,
"volumes": [
{
"name": "dev",
"hostPath": {
"path": "/dev"
}
},
{
"name": "nvidia-install-dir-host",
"hostPath": {
"path": "/home/kubernetes/bin/nvidia"
}
},
{
"name": "root-mount",
"hostPath": {
"path": "/"
}
}
],
"initContainers": [
{
"image": "cos-nvidia-installer:fixed",
"imagePullPolicy": "Never",
"name": "nvidia-driver-installer",
"resources": {
"requests": {
"cpu": 0.15
}
},
"securityContext": {
"privileged": true
},
"env": [
{
"name": "NVIDIA_INSTALL_DIR_HOST",
"value": "/home/kubernetes/bin/nvidia"
},
{
"name": "NVIDIA_INSTALL_DIR_CONTAINER",
"value": "/usr/local/nvidia"
},
{
"name": "ROOT_MOUNT_DIR",
"value": "/root"
}
],
"volumeMounts": [
{
"name": "nvidia-install-dir-host",
"mountPath": "/usr/local/nvidia"
},
{
"name": "dev",
"mountPath": "/dev"
},
{
"name": "root-mount",
"mountPath": "/root"
}
]
}
],
"containers": [
{
"image": "gcr.io/google-containers/pause:2.0",
"name": "pause"
}
]
}
}
}
},
daemonset:: daemonset,
parts:: self,
all:: [
self.daemonset,
],
list(obj=self.all):: k.core.v1.list.new(obj,),
},
}

View File

@ -0,0 +1,51 @@
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: default-routes
namespace: kubeflow
spec:
hosts:
- "*"
gateways:
- kubeflow-gateway
http:
- match:
- uri:
exact: /healthz
route:
- destination:
port:
number: 80
host: whoami-app.kubeflow.svc.cluster.local
- match:
- uri:
exact: /whoami
route:
- destination:
port:
number: 80
host: whoami-app.kubeflow.svc.cluster.local
- match:
- uri:
prefix: /
route:
- destination:
port:
number: 80
host: ambassador.kubeflow.svc.cluster.local
---
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: kubeflow-gateway
namespace: kubeflow
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More