Compare commits

...

127 Commits

Author SHA1 Message Date
Rush Tehrani
cb4a229984 Merge pull request #425 from onepanelio/fix/changing.secretAccountKey.key.to.be.consistent
fix: Use consistent naming convention for GCS artifactRepository secret key
2020-07-24 12:15:44 -07:00
Aleksandr Melnikov
c3f76f971c Removing comments. 2020-07-24 12:08:41 -07:00
Aleksandr Melnikov
dc27fd3319 Changing serviceAccountKey to be more consistent with other secret
keys.
2020-07-24 12:08:36 -07:00
rushtehrani
0a8744656c remove redeclared package 2020-07-24 11:51:49 -07:00
Rush Tehrani
1910a47a21 Merge pull request #423 from onepanelio/fix/putting.yaml.lib.back.for.nodepool.options
fix: Regression fix; We need the specific sigs.k8s.io/yaml library
2020-07-24 11:22:26 -07:00
Aleksandr Melnikov
e3fd781cd3 Merge branch 'dev' into fix/putting.yaml.lib.back.for.nodepool.options 2020-07-24 11:21:06 -07:00
Rush Tehrani
5eff42fb2c Merge pull request #422 from onepanelio/fix/adding.yaml.annotation.to.prevent.empty.keys
fix: Adding omitempty to prevent the keys from showing up in onepanel
2020-07-24 11:13:44 -07:00
Aleksandr Melnikov
3067c62a7f Regression fix; We need the specific sigs.k8s.io/yaml library
to properly unmarshal nodePoolOptions from params.yaml.
2020-07-23 21:09:37 -07:00
Aleksandr Melnikov
adb7f0b74a Adding omitempty to prevent the keys from showing up in onepanel
configmap, if they are not filled.
- Adding explicit yaml conversion for serviceAccountJSON, otherwise
"omitempty" shows in the configmap.
2020-07-23 20:45:37 -07:00
Rush Tehrani
bdc3d99fed Merge pull request #400 from onepanelio/feat/core.331-support.gcs.artifact.repository
feat: core.331 support.gcs.artifact.repository
2020-07-23 13:11:58 -07:00
Aleksandr Melnikov
aa6725fbc9 Another comment adjustment. 2020-07-22 17:00:14 -07:00
Aleksandr Melnikov
8da35695de Another comments adjustment. 2020-07-22 16:57:19 -07:00
Aleksandr Melnikov
fc7cdb5681 Another comments adjustment. 2020-07-22 16:46:14 -07:00
Aleksandr Melnikov
7bbeba544d Fixing comments per Codacy. 2020-07-22 16:37:55 -07:00
Aleksandr Melnikov
5b7f5c9724 Codacy feedback for comments. 2020-07-22 16:30:23 -07:00
Aleksandr Melnikov
1effb919d6 Per Codacy,
- Adding comments
- Refactoring variables names
- Fixing return variable order
2020-07-22 16:23:24 -07:00
Aleksandr Melnikov
7f6f58884a Per discussion, combining Config structs into Provider. 2020-07-22 16:01:33 -07:00
Aleksandr Melnikov
16274dd946 Moving some attributes form S3Config to S3Provider. 2020-07-22 15:32:38 -07:00
Aleksandr Melnikov
cebb412175 Removing extra yaml from import. 2020-07-22 15:31:58 -07:00
Aleksandr Melnikov
5dbb2104ce Merge branch 'dev' into feat/core.331-support.gcs.artifact.repository 2020-07-22 14:03:10 -07:00
Andrey Melnikov
2232d4e946 Merge pull request #417 from onepanelio/feat/onepanel.auth.header
feat: onepanel-auth-token header for authentication
2020-07-21 15:03:33 -07:00
Andrey Melnikov
e66c095500 update: added reserved workspace names 2020-07-21 14:37:51 -07:00
Andrey Melnikov
7af3c9dd7c feat: allowed onepanel-auth-token header to provide authentication token.
Updated server to make that key not require a grpc-gateway prefix.
2020-07-20 20:52:36 -07:00
Rush Tehrani
b01c0c41a8 Merge pull request #412 from onepanelio/fix/onepanelio.core.407-workspaces.missing.node.pools
fix: goose binary errors
2020-07-14 19:14:04 -07:00
Andrey Melnikov
59d0c95307 fix: extra run of goose up 2020-07-14 19:11:30 -07:00
Andrey Melnikov
d319d41929 fix: goose command since the package structure changed, we need to update it to reference the go migrations package. Further, we need to update the logic to run both migrations of sql and go. 2020-07-14 19:00:52 -07:00
Andrey Melnikov
9435404701 fix: filepath separator to include windows support. 2020-07-14 19:00:05 -07:00
Rush Tehrani
b6b8652829 Merge pull request #410 from onepanelio/fix/onepanelio.core.407-workspaces.missing.node.pools
fix: get workspaces now returns up-to-date node pool options
2020-07-14 18:39:22 -07:00
Andrey Melnikov
5073cbdff1 fix: issue where changing configmap crashed goose because migrations were re-added 2020-07-14 18:31:21 -07:00
Andrey Melnikov
0b77438d57 clean: drop nil initialization from var pointer as it is the default value. 2020-07-14 15:16:15 -07:00
Andrey Melnikov
59e7d58503 fix: get workspaces now returns up-to-date node pool options 2020-07-14 15:09:06 -07:00
Aleksandr Melnikov
fa96d6ef66 Merge branch 'dev' into feat/core.331-support.gcs.artifact.repository 2020-07-14 14:06:47 -07:00
Aleksandr Melnikov
6c0d3fe598 Refactored GCS client creation.
Added GetObject function to be consistent with S3 functionality.
Note
- pkg/client.go, line 88 passes in the JSON.
If ArtifactRepositoryGCSCondig was imported inside gcs.NewClient
code, an import cycle would be created.
2020-07-14 14:05:31 -07:00
Aleksandr Melnikov
b0361cdc6c Instead of passing the secret values into the configmap, pass
the secret reference and secret key.
2020-07-13 17:37:29 -07:00
Aleksandr Melnikov
e526505365 Fixing the Key to look for inside the secret. 2020-07-13 17:01:07 -07:00
Aleksandr Melnikov
993ce397aa Removing todo, verified Name is correct. 2020-07-13 16:02:14 -07:00
Aleksandr Melnikov
7a8df485ad Removing completed todos. 2020-07-10 17:53:33 -07:00
Aleksandr Melnikov
7936eac98d Adding a way to store the location of the secret and the secret key to use.
Adding another field to struct, which will store the ServiceAccountJSON.
- This will be generated by the API and used by the API for the GCS client.
2020-07-10 17:53:25 -07:00
Aleksandr Melnikov
94ae9071aa Adding exit condition to for true when iterating through bucket files. 2020-07-10 17:19:03 -07:00
Aleksandr Melnikov
8b6d10d112 Updating GCS MarshalToYaml
- Do not write the value of ServiceAccount to Config Map. This
exposes credentials.
- Properly write the reference the argo expects. The reference
points to the secret that contains the credentials.
2020-07-10 17:19:03 -07:00
Aleksandr Melnikov
a94ae526ca Merge pull request #393 from onepanelio/feat/provider-specific-config
feat: Add support for ResourceRequirements in node pools
2020-07-10 11:35:30 -07:00
Aleksandr Melnikov
8e2b1ab106 Merge branch 'dev' into feat/core.331-support.gcs.artifact.repository 2020-07-09 18:09:50 -07:00
Aleksandr Melnikov
e83e5d495e Injecting GCS configuration into ArtifactRepository. 2020-07-09 18:02:18 -07:00
Aleksandr Melnikov
0f5cf7be4d Fixing shadowed err. 2020-07-09 16:30:11 -07:00
Aleksandr Melnikov
7295ba36b3 Putting pack a required package. 2020-07-09 16:30:00 -07:00
rushtehrani
a8958b0136 move migration to correct folder 2020-07-09 15:35:06 -07:00
rushtehrani
69a6a72303 add missing methods after conflict resolution 2020-07-09 14:36:24 -07:00
Rush Tehrani
92f2be7cac Merge branch 'dev' into feat/provider-specific-config 2020-07-09 14:18:49 -07:00
Rush Tehrani
46f4465103 Merge pull request #374 from onepanelio/test/docker.database
feat: setup unit tests with database
2020-07-09 14:17:41 -07:00
rushtehrani
ca45e29dd5 remove unused file 2020-07-09 14:06:38 -07:00
rushtehrani
dfd892d6f1 update func names to match new convention 2020-07-09 13:50:42 -07:00
rushtehrani
c1d123ec2c use run-tests for all tests 2020-07-09 13:43:28 -07:00
rushtehrani
7c2001f5ac Merge branch 'test/docker.database' of https://github.com/onepanelio/core into test/docker.database 2020-07-09 12:45:23 -07:00
rushtehrani
aff166fb40 change test names to match new convention 2020-07-09 12:45:16 -07:00
Andrey Melnikov
40759738ae fix: took up the messages if a go migration was already ran as it was printed after the goose message, which was a bit confusing. 2020-07-09 12:27:44 -07:00
Andrey Melnikov
a0454cdfc3 fix: backwards compatibility for previous go migrations. Don't run them if they were ran earlier in the goose_db_version db table. 2020-07-09 12:23:38 -07:00
Rush Tehrani
41d3d7be34 Merge pull request #399 from onepanelio/fix/new-cvat-template
fix: Update CVAT template to Comment out filesyncer to avoid crashing
2020-07-09 11:46:31 -07:00
rushtehrani
4dc60c2113 update cvat template 2020-07-09 11:43:25 -07:00
Rush Tehrani
3cf11eaa58 Merge pull request #398 from onepanelio/revert-394-fix/cvat-template
Revert "fix: Comment out filesyncer to avoid CVAT template crashing"
2020-07-09 11:33:01 -07:00
Rush Tehrani
3b605545b1 Revert "fix: Comment out filesyncer to avoid CVAT template crashing" 2020-07-09 11:32:16 -07:00
Rush Tehrani
eef3309080 Merge pull request #394 from onepanelio/fix/cvat-template
fix: Comment out filesyncer to avoid CVAT template crashing
2020-07-09 11:27:11 -07:00
Aleksandr Melnikov
4e3eaae1f6 Updating ListFiles to support GCS. 2020-07-09 10:55:37 -07:00
rushtehrani
2ddab18a59 rename function 2020-07-08 17:50:16 -07:00
Aleksandr Melnikov
6727b00b05 Updating GetArtifact to support GCS. 2020-07-08 14:00:51 -07:00
Aleksandr Melnikov
81c05f9954 Adding error check. 2020-07-08 14:00:20 -07:00
Aleksandr Melnikov
ac5bf65ae9 Added GCS support for Metrics retrieval. 2020-07-08 13:36:59 -07:00
Aleksandr Melnikov
66431a21eb Added code to get the log object from GCS.
Added switch statement for GCS versus S3.
2020-07-08 13:17:07 -07:00
Aleksandr Melnikov
8d662d6ce0 Adding FormatKey function for GCS Config. 2020-07-08 13:16:42 -07:00
Aleksandr Melnikov
ad16285fd4 Fixing typo.
Adding explicit error checking for opts.SetRange.
2020-07-08 12:53:12 -07:00
Aleksandr Melnikov
f024889ca8 Implementing switch statement since we have s3 and gcs storage now.
- GCS has necessary credentials in serviceAccount as json
2020-07-08 12:52:55 -07:00
Aleksandr Melnikov
d7df089d31 When grabbing artifactRepository from config, check if
S3 or GCS are set.
- If not, throw an error.
2020-07-08 12:52:11 -07:00
Aleksandr Melnikov
2cd3f76299 Adding GetGCSClient function.
- Returns client that can interact with google cloud storage.
2020-07-08 12:51:45 -07:00
Aleksandr Melnikov
965e8dda92 Adding Google library for Google Cloud Storage. 2020-07-08 11:33:47 -07:00
Aleksandr Melnikov
c13c868143 Adding GCS structs for Artifact Repository configuration.
- These will be unmarshalled into via CLI from params.yaml.
- Added helper function, MarshalToYaml.
2020-07-06 21:56:39 -07:00
Aleksandr Melnikov
e2f94e937c Adding function to output the CLI required YAML for manifests.
- Adding helper structs and tags
2020-07-06 17:07:14 -07:00
Aleksandr Melnikov
df74766a66 Adding yaml tags for unmarshalling into the struct. 2020-07-06 17:06:40 -07:00
Aleksandr Melnikov
30305f42e1 Adding Yaml V3. 2020-07-06 17:04:12 -07:00
rushtehrani
41b2f322da update cvat version 2020-07-04 23:13:39 -07:00
rushtehrani
946ab11dab fix: comment out filesyncer and reference docs 2020-07-04 15:28:33 -07:00
rushtehrani
886e39244e add comments to exported structs and methods 2020-07-03 11:43:52 -07:00
rushtehrani
7ce8d9cdfa support workflow params for resource injection 2020-07-03 11:38:04 -07:00
rushtehrani
2868ab69c9 add resources requests and limits to nodePoolOptions 2020-07-02 22:29:05 -07:00
rushtehrani
18428c4e82 add nvidia-smi in case of aks 2020-07-02 19:19:27 -07:00
Andrey Melnikov
df3dba4e2e test: Added basic test for CreateWorkspace (capital) 2020-07-02 16:45:52 -07:00
Andrey Melnikov
d097cdfefe clean: removed UpdateWorkflowTemplateVersion as it did nothing and should not be used.
#breaking-change
2020-07-02 16:45:34 -07:00
rushtehrani
cee49e67f4 add ONEPANEL_PROVIDER env var 2020-07-02 16:44:22 -07:00
Andrey Melnikov
640c7b54f5 test: fixed issue where Updating the workspace status did not correctly detect a not-found condition and added tests for it. 2020-07-02 16:22:23 -07:00
Andrey Melnikov
655020b54b clean: removed unnecessary generation of uid in "injectWorkspaceSystemParameters" function.
Updated time tests to compare against time.Time{} because nanosecond difference comparison doesn't always work.
2020-07-02 15:46:46 -07:00
Andrey Melnikov
5117c8b34e tests: separated out logic to convert workspace status to a field map and added tests for it 2020-07-02 15:41:54 -07:00
Andrey Melnikov
045912fe5c tests: added more tests surround workspace templates and workspaces 2020-07-02 13:21:11 -07:00
Andrey Melnikov
3673b3da59 tests: added more tests for workspace templates 2020-07-02 12:19:39 -07:00
Andrey Melnikov
d7db598cc6 clean: removed unused methods 2020-07-02 12:19:15 -07:00
Andrey Melnikov
f3a2856d65 fix: codacy issues 2020-07-02 11:20:18 -07:00
Andrey Melnikov
f1045710a0 update: refactored creating a workflow execution 2020-07-02 11:17:31 -07:00
Andrey Melnikov
ed621633df update: updated tests to have method calls for particular cases. Added more tests. 2020-07-01 16:07:20 -07:00
Andrey Melnikov
c5d1ec9b1f fix: go migrations since they would still occur in the order of creation timestamp. Manually initializing them fixes this issue. 2020-07-01 16:06:18 -07:00
Andrey Melnikov
5c225b405d clean: reworked creating a workflow template and version to consolidate logic 2020-06-30 13:05:28 -07:00
Andrey Melnikov
bd5ec0e7c2 fix: updated versions to use a bigint to store nanosecond precision.
updated code to reflect this change and migrated it over.
2020-06-30 11:42:07 -07:00
Andrey Melnikov
176fcc3008 update: recreate issue with workflow template versions being created one after another 2020-06-30 10:59:32 -07:00
Andrey Melnikov
945b3bc2e5 fix: moved migration into appropriate folder 2020-06-30 10:50:35 -07:00
Andrey Melnikov
1bdd6a7eda Merge branch 'test/docker.database' of github.com:onepanelio/core into test/docker.database 2020-06-30 10:50:05 -07:00
Andrey Melnikov
f78ae63afa clean: added method to SysConfig to return database connection information 2020-06-30 10:49:22 -07:00
Andrey Melnikov
114f290cde docs: documented some workflow execution methods 2020-06-30 10:49:22 -07:00
Andrey Melnikov
1a15df4999 fix: testing commit for db connection 2020-06-30 10:49:22 -07:00
Andrey Melnikov
f9f6d75be8 fix: remove extraneous character 2020-06-30 10:49:22 -07:00
Andrey Melnikov
782617a3e9 clean: clean up code for cron workflows 2020-06-30 10:49:22 -07:00
Andrey Melnikov
0ec109da2b fix: how we get argo mock. 2020-06-30 10:49:22 -07:00
Andrey Melnikov
b8b9ff2543 clean: capitalization of names and added WorkflowTemplate uid generator method to encapsulate the behavior 2020-06-30 10:49:22 -07:00
Andrey Melnikov
0b54bd1c43 test: added tests for workflow executions and creating workflow templates 2020-06-30 10:49:22 -07:00
Andrey Melnikov
cb27809665 fix: dynamic database service name in testing 2020-06-30 10:49:22 -07:00
Andrey Melnikov
252e53a793 feat: added flag to database service for testing 2020-06-30 10:49:22 -07:00
Andrey Melnikov
05fc2b1422 fix: inconsistent testing variables and github action variables 2020-06-30 10:49:22 -07:00
Andrey Melnikov
839b9ac2e3 clean: removed custom configmap/secret setting as providing the objects to k8s mock will have them be automatically loaded. 2020-06-30 10:49:22 -07:00
Andrey Melnikov
2091485345 feat: added basic unit testing. Also separated migrations to separate sql and go migrations. This makes it easier to unit test where we just need sql migrations. 2020-06-30 10:49:22 -07:00
Andrey Melnikov
ec2f9b3ecd clean: added method to SysConfig to return database connection information 2020-06-26 17:35:24 -07:00
Andrey Melnikov
a2c11050fc docs: documented some workflow execution methods 2020-06-26 17:26:11 -07:00
Andrey Melnikov
b3f9c44482 fix: testing commit for db connection 2020-06-26 17:25:45 -07:00
Andrey Melnikov
8aa10c9630 fix: remove extraneous character 2020-06-26 17:17:33 -07:00
Andrey Melnikov
6c8f6cee5b clean: clean up code for cron workflows 2020-06-26 17:15:01 -07:00
Andrey Melnikov
60bfe4e02a fix: how we get argo mock. 2020-06-26 17:14:31 -07:00
Andrey Melnikov
9e022b24f7 clean: capitalization of names and added WorkflowTemplate uid generator method to encapsulate the behavior 2020-06-26 16:14:07 -07:00
Andrey Melnikov
be63c412be test: added tests for workflow executions and creating workflow templates 2020-06-26 16:11:57 -07:00
Andrey Melnikov
62f53ee605 fix: dynamic database service name in testing 2020-06-25 16:51:18 -07:00
Andrey Melnikov
81241b0cc9 feat: added flag to database service for testing 2020-06-25 16:48:22 -07:00
Andrey Melnikov
90602b8f87 fix: inconsistent testing variables and github action variables 2020-06-25 16:34:42 -07:00
Andrey Melnikov
ff2e948623 clean: removed custom configmap/secret setting as providing the objects to k8s mock will have them be automatically loaded. 2020-06-25 16:30:10 -07:00
Andrey Melnikov
a831ed164d feat: added basic unit testing. Also separated migrations to separate sql and go migrations. This makes it easier to unit test where we just need sql migrations. 2020-06-25 16:27:33 -07:00
70 changed files with 2735 additions and 1106 deletions

26
.github/workflows/run_unit_tests.yaml vendored Normal file
View File

@@ -0,0 +1,26 @@
name: Run Unit Tests
on:
push:
branches:
- test/docker.database
jobs:
test-code-job:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:12.3
env:
POSTGRES_DB: onepanel
POSTGRES_USER: admin
POSTGRES_PASSWORD: tester
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@master
- name: Run testing code
uses: cedrickring/golang-action@1.5.2
with:
args: go test github.com/onepanelio/core/pkg -db=postgres

View File

@@ -34,3 +34,8 @@ docker-push:
docker push onepanel/core:$(COMMIT_HASH)
docker: docker-build docker-push
run-tests:
docker run --rm --name test-onepanel-postgres -p 5432:5432 -e POSTGRES_USER=admin -e POSTGRES_PASSWORD=tester -e POSTGRES_DB=onepanel -d postgres:12.3
go test github.com/onepanelio/core/pkg -count=1 ||:
docker stop test-onepanel-postgres

View File

@@ -1707,57 +1707,6 @@
]
}
},
"/apis/v1beta1/{namespace}/workflow_templates/{workflowTemplate.uid}/versions/{workflowTemplate.version}": {
"put": {
"operationId": "UpdateWorkflowTemplateVersion",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/WorkflowTemplate"
}
},
"default": {
"description": "An unexpected error response",
"schema": {
"$ref": "#/definitions/grpc.gateway.runtime.Error"
}
}
},
"parameters": [
{
"name": "namespace",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "workflowTemplate.uid",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "workflowTemplate.version",
"in": "path",
"required": true,
"type": "string",
"format": "int64"
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/WorkflowTemplate"
}
}
],
"tags": [
"WorkflowTemplateService"
]
}
},
"/apis/v1beta1/{namespace}/workspace_templates": {
"get": {
"operationId": "ListWorkspaceTemplates",

View File

@@ -1093,7 +1093,7 @@ var file_workflow_template_proto_rawDesc = []byte{
0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x76, 0x65,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x32, 0xa3, 0x0c, 0x0a, 0x17, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x32, 0xbb, 0x0a, 0x0a, 0x17, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c,
0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
0x65, 0x12, 0x9b, 0x01, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b,
0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x22, 0x2e, 0x61,
@@ -1105,94 +1105,79 @@ var file_workflow_template_proto_rawDesc = []byte{
0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x7d, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x66,
0x6c, 0x6f, 0x77, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x3a, 0x10, 0x77,
0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12,
0xe5, 0x01, 0x0a, 0x1d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c,
0xc2, 0x01, 0x0a, 0x1d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c,
0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
0x6e, 0x12, 0x29, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f,
0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x65,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c,
0x61, 0x74, 0x65, 0x22, 0x81, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x7b, 0x1a, 0x67, 0x2f, 0x61,
0x70, 0x69, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d,
0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x7d, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x77, 0x6f, 0x72, 0x6b,
0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x2e, 0x75, 0x69, 0x64,
0x7d, 0x2f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x77, 0x6f, 0x72, 0x6b,
0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x7d, 0x3a, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54,
0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0xc2, 0x01, 0x0a, 0x1d, 0x43, 0x72, 0x65, 0x61,
0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61,
0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e,
0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65,
0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70,
0x6c, 0x61, 0x74, 0x65, 0x22, 0x66, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x60, 0x22, 0x4c, 0x2f, 0x61,
0x70, 0x69, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d,
0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x7d, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x77, 0x6f, 0x72, 0x6b,
0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x2e, 0x75, 0x69, 0x64,
0x7d, 0x2f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x3a, 0x10, 0x77, 0x6f, 0x72, 0x6b,
0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0xd3, 0x01, 0x0a,
0x13, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70,
0x6c, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f,
0x6e, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f,
0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x57, 0x6f, 0x72, 0x6b,
0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x22, 0x83, 0x01, 0x82,
0xd3, 0xe4, 0x93, 0x02, 0x7d, 0x12, 0x32, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x76, 0x31, 0x62,
0x65, 0x74, 0x61, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x7d,
0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61,
0x74, 0x65, 0x73, 0x2f, 0x7b, 0x75, 0x69, 0x64, 0x7d, 0x5a, 0x47, 0x12, 0x45, 0x2f, 0x61, 0x70,
0x69, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65,
0x73, 0x70, 0x61, 0x63, 0x65, 0x7d, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f,
0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x75, 0x69, 0x64, 0x7d, 0x2f,
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
0x6e, 0x7d, 0x12, 0xb8, 0x01, 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66,
0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, 0x73, 0x12, 0x28, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f,
0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x65,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x43, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3d,
0x12, 0x3b, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f,
0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x7d, 0x2f, 0x77, 0x6f, 0x72, 0x6b,
0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x7b,
0x75, 0x69, 0x64, 0x7d, 0x2f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x94, 0x01,
0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65,
0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69,
0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61,
0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x61, 0x70, 0x69,
0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d,
0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x34,
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2e, 0x12, 0x2c, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x76, 0x31,
0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65,
0x7d, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c,
0x61, 0x74, 0x65, 0x73, 0x12, 0xe9, 0x01, 0x0a, 0x15, 0x43, 0x6c, 0x6f, 0x6e, 0x65, 0x57, 0x6f,
0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x21,
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6c, 0x6f, 0x6e, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c,
0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x22, 0x95, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02,
0x8e, 0x01, 0x12, 0x3f, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61,
0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x7d, 0x2f, 0x77, 0x6f,
0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73,
0x2f, 0x7b, 0x75, 0x69, 0x64, 0x7d, 0x2f, 0x63, 0x6c, 0x6f, 0x6e, 0x65, 0x2f, 0x7b, 0x6e, 0x61,
0x6d, 0x65, 0x7d, 0x5a, 0x4b, 0x12, 0x49, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x76, 0x31, 0x62,
0x65, 0x74, 0x61, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x7d,
0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61,
0x74, 0x65, 0x73, 0x2f, 0x7b, 0x75, 0x69, 0x64, 0x7d, 0x2f, 0x63, 0x6c, 0x6f, 0x6e, 0x65, 0x2f,
0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x7b, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x7d,
0x12, 0xa8, 0x01, 0x0a, 0x17, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x57, 0x6f, 0x72, 0x6b,
0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x23, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c,
0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x57,
0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x42, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3c, 0x1a,
0x3a, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x7b,
0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x7d, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x66,
0x6c, 0x6f, 0x77, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x75,
0x69, 0x64, 0x7d, 0x2f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x22, 0x66, 0x82, 0xd3,
0xe4, 0x93, 0x02, 0x60, 0x22, 0x4c, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65,
0x74, 0x61, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x7d, 0x2f,
0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74,
0x65, 0x73, 0x2f, 0x7b, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70,
0x6c, 0x61, 0x74, 0x65, 0x2e, 0x75, 0x69, 0x64, 0x7d, 0x2f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
0x6e, 0x73, 0x3a, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70,
0x6c, 0x61, 0x74, 0x65, 0x12, 0xd3, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b,
0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65,
0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70,
0x6c, 0x61, 0x74, 0x65, 0x22, 0x83, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x7d, 0x12, 0x32, 0x2f,
0x61, 0x70, 0x69, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x7b, 0x6e, 0x61,
0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x7d, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f,
0x77, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x75, 0x69, 0x64,
0x7d, 0x5a, 0x47, 0x12, 0x45, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74,
0x61, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x7d, 0x2f, 0x77,
0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65,
0x73, 0x2f, 0x7b, 0x75, 0x69, 0x64, 0x7d, 0x2f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73,
0x2f, 0x7b, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x7d, 0x12, 0xb8, 0x01, 0x0a, 0x1c, 0x4c,
0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c,
0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x2e, 0x61, 0x70,
0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65,
0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74,
0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65,
0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x43, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3d, 0x12, 0x3b, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f,
0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61,
0x63, 0x65, 0x7d, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x74, 0x65, 0x6d,
0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x75, 0x69, 0x64, 0x7d, 0x2f, 0x76, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x94, 0x01, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f,
0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12,
0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c,
0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72,
0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x34, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2e, 0x12, 0x2c,
0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x7b, 0x6e,
0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x7d, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c,
0x6f, 0x77, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12, 0xe9, 0x01, 0x0a,
0x15, 0x43, 0x6c, 0x6f, 0x6e, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65,
0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6c, 0x6f,
0x6e, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61,
0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e,
0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65,
0x22, 0x95, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x8e, 0x01, 0x12, 0x3f, 0x2f, 0x61, 0x70, 0x69,
0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73,
0x70, 0x61, 0x63, 0x65, 0x7d, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x74,
0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x75, 0x69, 0x64, 0x7d, 0x2f, 0x63,
0x6c, 0x6f, 0x6e, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x5a, 0x4b, 0x12, 0x49, 0x2f,
0x61, 0x70, 0x69, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x7b, 0x6e, 0x61,
0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x7d, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f,
0x77, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x75, 0x69, 0x64,
0x7d, 0x2f, 0x63, 0x6c, 0x6f, 0x6e, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x7b,
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x7d, 0x12, 0xa8, 0x01, 0x0a, 0x17, 0x41, 0x72, 0x63,
0x68, 0x69, 0x76, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70,
0x6c, 0x61, 0x74, 0x65, 0x12, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x72, 0x63, 0x68, 0x69,
0x76, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61,
0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e,
0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54,
0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x42, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3c, 0x1a, 0x3a, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x76,
0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63,
0x65, 0x7d, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x74, 0x65, 0x6d, 0x70,
0x6c, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x75, 0x69, 0x64, 0x7d, 0x2f, 0x61, 0x72, 0x63, 0x68,
0x69, 0x76, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -1235,23 +1220,21 @@ var file_workflow_template_proto_depIdxs = []int32{
10, // 6: api.WorkflowTemplate.stats:type_name -> api.WorkflowExecutionStatisticReport
11, // 7: api.WorkflowTemplate.cronStats:type_name -> api.CronWorkflowStatisticsReport
0, // 8: api.WorkflowTemplateService.CreateWorkflowTemplate:input_type -> api.CreateWorkflowTemplateRequest
1, // 9: api.WorkflowTemplateService.UpdateWorkflowTemplateVersion:input_type -> api.UpdateWorkflowTemplateVersionRequest
0, // 10: api.WorkflowTemplateService.CreateWorkflowTemplateVersion:input_type -> api.CreateWorkflowTemplateRequest
2, // 11: api.WorkflowTemplateService.GetWorkflowTemplate:input_type -> api.GetWorkflowTemplateRequest
4, // 12: api.WorkflowTemplateService.ListWorkflowTemplateVersions:input_type -> api.ListWorkflowTemplateVersionsRequest
6, // 13: api.WorkflowTemplateService.ListWorkflowTemplates:input_type -> api.ListWorkflowTemplatesRequest
3, // 14: api.WorkflowTemplateService.CloneWorkflowTemplate:input_type -> api.CloneWorkflowTemplateRequest
8, // 15: api.WorkflowTemplateService.ArchiveWorkflowTemplate:input_type -> api.ArchiveWorkflowTemplateRequest
12, // 16: api.WorkflowTemplateService.CreateWorkflowTemplate:output_type -> api.WorkflowTemplate
12, // 17: api.WorkflowTemplateService.UpdateWorkflowTemplateVersion:output_type -> api.WorkflowTemplate
12, // 18: api.WorkflowTemplateService.CreateWorkflowTemplateVersion:output_type -> api.WorkflowTemplate
12, // 19: api.WorkflowTemplateService.GetWorkflowTemplate:output_type -> api.WorkflowTemplate
5, // 20: api.WorkflowTemplateService.ListWorkflowTemplateVersions:output_type -> api.ListWorkflowTemplateVersionsResponse
7, // 21: api.WorkflowTemplateService.ListWorkflowTemplates:output_type -> api.ListWorkflowTemplatesResponse
12, // 22: api.WorkflowTemplateService.CloneWorkflowTemplate:output_type -> api.WorkflowTemplate
9, // 23: api.WorkflowTemplateService.ArchiveWorkflowTemplate:output_type -> api.ArchiveWorkflowTemplateResponse
16, // [16:24] is the sub-list for method output_type
8, // [8:16] is the sub-list for method input_type
0, // 9: api.WorkflowTemplateService.CreateWorkflowTemplateVersion:input_type -> api.CreateWorkflowTemplateRequest
2, // 10: api.WorkflowTemplateService.GetWorkflowTemplate:input_type -> api.GetWorkflowTemplateRequest
4, // 11: api.WorkflowTemplateService.ListWorkflowTemplateVersions:input_type -> api.ListWorkflowTemplateVersionsRequest
6, // 12: api.WorkflowTemplateService.ListWorkflowTemplates:input_type -> api.ListWorkflowTemplatesRequest
3, // 13: api.WorkflowTemplateService.CloneWorkflowTemplate:input_type -> api.CloneWorkflowTemplateRequest
8, // 14: api.WorkflowTemplateService.ArchiveWorkflowTemplate:input_type -> api.ArchiveWorkflowTemplateRequest
12, // 15: api.WorkflowTemplateService.CreateWorkflowTemplate:output_type -> api.WorkflowTemplate
12, // 16: api.WorkflowTemplateService.CreateWorkflowTemplateVersion:output_type -> api.WorkflowTemplate
12, // 17: api.WorkflowTemplateService.GetWorkflowTemplate:output_type -> api.WorkflowTemplate
5, // 18: api.WorkflowTemplateService.ListWorkflowTemplateVersions:output_type -> api.ListWorkflowTemplateVersionsResponse
7, // 19: api.WorkflowTemplateService.ListWorkflowTemplates:output_type -> api.ListWorkflowTemplatesResponse
12, // 20: api.WorkflowTemplateService.CloneWorkflowTemplate:output_type -> api.WorkflowTemplate
9, // 21: api.WorkflowTemplateService.ArchiveWorkflowTemplate:output_type -> api.ArchiveWorkflowTemplateResponse
15, // [15:22] is the sub-list for method output_type
8, // [8:15] is the sub-list for method input_type
8, // [8:8] is the sub-list for extension type_name
8, // [8:8] is the sub-list for extension extendee
0, // [0:8] is the sub-list for field type_name
@@ -1466,7 +1449,6 @@ const _ = grpc.SupportPackageIsVersion6
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type WorkflowTemplateServiceClient interface {
CreateWorkflowTemplate(ctx context.Context, in *CreateWorkflowTemplateRequest, opts ...grpc.CallOption) (*WorkflowTemplate, error)
UpdateWorkflowTemplateVersion(ctx context.Context, in *UpdateWorkflowTemplateVersionRequest, opts ...grpc.CallOption) (*WorkflowTemplate, error)
CreateWorkflowTemplateVersion(ctx context.Context, in *CreateWorkflowTemplateRequest, opts ...grpc.CallOption) (*WorkflowTemplate, error)
GetWorkflowTemplate(ctx context.Context, in *GetWorkflowTemplateRequest, opts ...grpc.CallOption) (*WorkflowTemplate, error)
ListWorkflowTemplateVersions(ctx context.Context, in *ListWorkflowTemplateVersionsRequest, opts ...grpc.CallOption) (*ListWorkflowTemplateVersionsResponse, error)
@@ -1492,15 +1474,6 @@ func (c *workflowTemplateServiceClient) CreateWorkflowTemplate(ctx context.Conte
return out, nil
}
func (c *workflowTemplateServiceClient) UpdateWorkflowTemplateVersion(ctx context.Context, in *UpdateWorkflowTemplateVersionRequest, opts ...grpc.CallOption) (*WorkflowTemplate, error) {
out := new(WorkflowTemplate)
err := c.cc.Invoke(ctx, "/api.WorkflowTemplateService/UpdateWorkflowTemplateVersion", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *workflowTemplateServiceClient) CreateWorkflowTemplateVersion(ctx context.Context, in *CreateWorkflowTemplateRequest, opts ...grpc.CallOption) (*WorkflowTemplate, error) {
out := new(WorkflowTemplate)
err := c.cc.Invoke(ctx, "/api.WorkflowTemplateService/CreateWorkflowTemplateVersion", in, out, opts...)
@@ -1558,7 +1531,6 @@ func (c *workflowTemplateServiceClient) ArchiveWorkflowTemplate(ctx context.Cont
// WorkflowTemplateServiceServer is the server API for WorkflowTemplateService service.
type WorkflowTemplateServiceServer interface {
CreateWorkflowTemplate(context.Context, *CreateWorkflowTemplateRequest) (*WorkflowTemplate, error)
UpdateWorkflowTemplateVersion(context.Context, *UpdateWorkflowTemplateVersionRequest) (*WorkflowTemplate, error)
CreateWorkflowTemplateVersion(context.Context, *CreateWorkflowTemplateRequest) (*WorkflowTemplate, error)
GetWorkflowTemplate(context.Context, *GetWorkflowTemplateRequest) (*WorkflowTemplate, error)
ListWorkflowTemplateVersions(context.Context, *ListWorkflowTemplateVersionsRequest) (*ListWorkflowTemplateVersionsResponse, error)
@@ -1574,9 +1546,6 @@ type UnimplementedWorkflowTemplateServiceServer struct {
func (*UnimplementedWorkflowTemplateServiceServer) CreateWorkflowTemplate(context.Context, *CreateWorkflowTemplateRequest) (*WorkflowTemplate, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateWorkflowTemplate not implemented")
}
func (*UnimplementedWorkflowTemplateServiceServer) UpdateWorkflowTemplateVersion(context.Context, *UpdateWorkflowTemplateVersionRequest) (*WorkflowTemplate, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateWorkflowTemplateVersion not implemented")
}
func (*UnimplementedWorkflowTemplateServiceServer) CreateWorkflowTemplateVersion(context.Context, *CreateWorkflowTemplateRequest) (*WorkflowTemplate, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateWorkflowTemplateVersion not implemented")
}
@@ -1618,24 +1587,6 @@ func _WorkflowTemplateService_CreateWorkflowTemplate_Handler(srv interface{}, ct
return interceptor(ctx, in, info, handler)
}
func _WorkflowTemplateService_UpdateWorkflowTemplateVersion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdateWorkflowTemplateVersionRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(WorkflowTemplateServiceServer).UpdateWorkflowTemplateVersion(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.WorkflowTemplateService/UpdateWorkflowTemplateVersion",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(WorkflowTemplateServiceServer).UpdateWorkflowTemplateVersion(ctx, req.(*UpdateWorkflowTemplateVersionRequest))
}
return interceptor(ctx, in, info, handler)
}
func _WorkflowTemplateService_CreateWorkflowTemplateVersion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateWorkflowTemplateRequest)
if err := dec(in); err != nil {
@@ -1752,10 +1703,6 @@ var _WorkflowTemplateService_serviceDesc = grpc.ServiceDesc{
MethodName: "CreateWorkflowTemplate",
Handler: _WorkflowTemplateService_CreateWorkflowTemplate_Handler,
},
{
MethodName: "UpdateWorkflowTemplateVersion",
Handler: _WorkflowTemplateService_UpdateWorkflowTemplateVersion_Handler,
},
{
MethodName: "CreateWorkflowTemplateVersion",
Handler: _WorkflowTemplateService_CreateWorkflowTemplateVersion_Handler,

View File

@@ -101,120 +101,6 @@ func local_request_WorkflowTemplateService_CreateWorkflowTemplate_0(ctx context.
}
func request_WorkflowTemplateService_UpdateWorkflowTemplateVersion_0(ctx context.Context, marshaler runtime.Marshaler, client WorkflowTemplateServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq UpdateWorkflowTemplateVersionRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.WorkflowTemplate); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["namespace"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "namespace")
}
protoReq.Namespace, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "namespace", err)
}
val, ok = pathParams["workflowTemplate.uid"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "workflowTemplate.uid")
}
err = runtime.PopulateFieldFromPath(&protoReq, "workflowTemplate.uid", val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "workflowTemplate.uid", err)
}
val, ok = pathParams["workflowTemplate.version"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "workflowTemplate.version")
}
err = runtime.PopulateFieldFromPath(&protoReq, "workflowTemplate.version", val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "workflowTemplate.version", err)
}
msg, err := client.UpdateWorkflowTemplateVersion(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_WorkflowTemplateService_UpdateWorkflowTemplateVersion_0(ctx context.Context, marshaler runtime.Marshaler, server WorkflowTemplateServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq UpdateWorkflowTemplateVersionRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.WorkflowTemplate); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["namespace"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "namespace")
}
protoReq.Namespace, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "namespace", err)
}
val, ok = pathParams["workflowTemplate.uid"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "workflowTemplate.uid")
}
err = runtime.PopulateFieldFromPath(&protoReq, "workflowTemplate.uid", val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "workflowTemplate.uid", err)
}
val, ok = pathParams["workflowTemplate.version"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "workflowTemplate.version")
}
err = runtime.PopulateFieldFromPath(&protoReq, "workflowTemplate.version", val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "workflowTemplate.version", err)
}
msg, err := server.UpdateWorkflowTemplateVersion(ctx, &protoReq)
return msg, metadata, err
}
func request_WorkflowTemplateService_CreateWorkflowTemplateVersion_0(ctx context.Context, marshaler runtime.Marshaler, client WorkflowTemplateServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq CreateWorkflowTemplateRequest
var metadata runtime.ServerMetadata
@@ -975,26 +861,6 @@ func RegisterWorkflowTemplateServiceHandlerServer(ctx context.Context, mux *runt
})
mux.Handle("PUT", pattern_WorkflowTemplateService_UpdateWorkflowTemplateVersion_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_WorkflowTemplateService_UpdateWorkflowTemplateVersion_0(rctx, inboundMarshaler, server, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_WorkflowTemplateService_UpdateWorkflowTemplateVersion_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_WorkflowTemplateService_CreateWorkflowTemplateVersion_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
@@ -1216,26 +1082,6 @@ func RegisterWorkflowTemplateServiceHandlerClient(ctx context.Context, mux *runt
})
mux.Handle("PUT", pattern_WorkflowTemplateService_UpdateWorkflowTemplateVersion_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_WorkflowTemplateService_UpdateWorkflowTemplateVersion_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_WorkflowTemplateService_UpdateWorkflowTemplateVersion_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_WorkflowTemplateService_CreateWorkflowTemplateVersion_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
@@ -1402,8 +1248,6 @@ func RegisterWorkflowTemplateServiceHandlerClient(ctx context.Context, mux *runt
var (
pattern_WorkflowTemplateService_CreateWorkflowTemplate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"apis", "v1beta1", "namespace", "workflow_templates"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_WorkflowTemplateService_UpdateWorkflowTemplateVersion_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6}, []string{"apis", "v1beta1", "namespace", "workflow_templates", "workflowTemplate.uid", "versions", "workflowTemplate.version"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_WorkflowTemplateService_CreateWorkflowTemplateVersion_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"apis", "v1beta1", "namespace", "workflow_templates", "workflowTemplate.uid", "versions"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_WorkflowTemplateService_GetWorkflowTemplate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"apis", "v1beta1", "namespace", "workflow_templates", "uid"}, "", runtime.AssumeColonVerbOpt(true)))
@@ -1424,8 +1268,6 @@ var (
var (
forward_WorkflowTemplateService_CreateWorkflowTemplate_0 = runtime.ForwardResponseMessage
forward_WorkflowTemplateService_UpdateWorkflowTemplateVersion_0 = runtime.ForwardResponseMessage
forward_WorkflowTemplateService_CreateWorkflowTemplateVersion_0 = runtime.ForwardResponseMessage
forward_WorkflowTemplateService_GetWorkflowTemplate_0 = runtime.ForwardResponseMessage

View File

@@ -13,13 +13,6 @@ service WorkflowTemplateService {
};
}
rpc UpdateWorkflowTemplateVersion (UpdateWorkflowTemplateVersionRequest) returns (WorkflowTemplate) {
option (google.api.http) = {
put: "/apis/v1beta1/{namespace}/workflow_templates/{workflowTemplate.uid}/versions/{workflowTemplate.version}"
body: "workflowTemplate"
};
}
rpc CreateWorkflowTemplateVersion (CreateWorkflowTemplateRequest) returns (WorkflowTemplate) {
option (google.api.http) = {
post: "/apis/v1beta1/{namespace}/workflow_templates/{workflowTemplate.uid}/versions"

View File

@@ -4,12 +4,12 @@ package main
import (
"flag"
"fmt"
"github.com/jmoiron/sqlx"
_ "github.com/onepanelio/core/db"
migrations "github.com/onepanelio/core/db/go"
v1 "github.com/onepanelio/core/pkg"
"log"
"os"
"path/filepath"
"github.com/pressly/goose"
)
@@ -38,10 +38,8 @@ func main() {
log.Fatalf("Failed to get system config: %v", err)
}
databaseDataSourceName := fmt.Sprintf("host=%v user=%v password=%v dbname=%v sslmode=disable",
config["databaseHost"], config["databaseUsername"], config["databasePassword"], config["databaseName"])
db := sqlx.MustConnect(config["databaseDriverName"], databaseDataSourceName)
dbDriverName, dbDataSourceName := config.DatabaseConnection()
db := sqlx.MustConnect(dbDriverName, dbDataSourceName)
command := args[0]
@@ -50,7 +48,14 @@ func main() {
arguments = append(arguments, args[2:]...)
}
if err := goose.Run(command, db.DB, *dir, arguments...); err != nil {
log.Fatalf("goose %v: %v", command, err)
goose.SetTableName("goose_db_version")
if err := goose.Run(command, db.DB, filepath.Join(*dir, "sql"), arguments...); err != nil {
log.Fatalf("Failed to run database sql migrations: %v %v", command, err)
}
goose.SetTableName("goose_db_go_version")
migrations.Initialize()
if err := goose.Run(command, db.DB, filepath.Join(*dir, "go"), arguments...); err != nil {
log.Fatalf("Failed to run database go migrations: %v %v", command, err)
}
}

View File

@@ -1,25 +0,0 @@
package migration
import (
"fmt"
"github.com/jmoiron/sqlx"
v1 "github.com/onepanelio/core/pkg"
)
func getClient() (*v1.Client, error) {
kubeConfig := v1.NewConfig()
client, err := v1.NewClient(kubeConfig, nil, nil)
if err != nil {
return nil, err
}
config, err := client.GetSystemConfig()
if err != nil {
return nil, err
}
databaseDataSourceName := fmt.Sprintf("host=%v user=%v password=%v dbname=%v sslmode=disable",
config["databaseHost"], config["databaseUsername"], config["databasePassword"], config["databaseName"])
client.DB = v1.NewDB(sqlx.MustConnect(config["databaseDriverName"], databaseDataSourceName))
return client, nil
}

View File

@@ -71,8 +71,11 @@ routes:
const jupyterLabTemplateName = "JupyterLab"
func init() {
goose.AddMigration(Up20200525160514, Down20200525160514)
func initialize20200525160514() {
if _, ok := initializedMigrations[20200525160514]; !ok {
goose.AddMigration(Up20200525160514, Down20200525160514)
initializedMigrations[20200525160514] = true
}
}
func Up20200525160514(tx *sql.Tx) error {
@@ -81,6 +84,15 @@ func Up20200525160514(tx *sql.Tx) error {
return err
}
migrationsRan, err := getRanSQLMigrations(client)
if err != nil {
return err
}
if _, ok := migrationsRan[20200525160514]; ok {
return nil
}
namespaces, err := client.ListOnepanelEnabledNamespaces()
if err != nil {
return err

View File

@@ -110,8 +110,11 @@ routes:
const cvatTemplateName = "CVAT"
func init() {
goose.AddMigration(Up20200528140124, Down20200528140124)
func initialize20200528140124() {
if _, ok := initializedMigrations[20200528140124]; !ok {
goose.AddMigration(Up20200528140124, Down20200528140124)
initializedMigrations[20200528140124] = true
}
}
// Up20200528140124 will insert the cvatTemplate to each user.
@@ -125,6 +128,15 @@ func Up20200528140124(tx *sql.Tx) error {
return err
}
migrationsRan, err := getRanSQLMigrations(client)
if err != nil {
return err
}
if _, ok := migrationsRan[20200528140124]; ok {
return nil
}
namespaces, err := client.ListOnepanelEnabledNamespaces()
if err != nil {
return err

View File

@@ -88,8 +88,11 @@ templates:
const pytorchMnistWorkflowTemplateName = "PyTorch Training"
func init() {
goose.AddMigration(Up20200605090509, Down20200605090509)
func initialize20200605090509() {
if _, ok := initializedMigrations[20200605090509]; !ok {
goose.AddMigration(Up20200605090509, Down20200605090509)
initializedMigrations[20200605090509] = true
}
}
// Up20200605090509 will insert a Pytorch workflow template to each user.
@@ -101,6 +104,15 @@ func Up20200605090509(tx *sql.Tx) error {
return err
}
migrationsRan, err := getRanSQLMigrations(client)
if err != nil {
return err
}
if _, ok := migrationsRan[20200605090509]; ok {
return nil
}
namespaces, err := client.ListOnepanelEnabledNamespaces()
if err != nil {
return err

View File

@@ -88,8 +88,11 @@ templates:
const tensorflowWorkflowTemplateName = "TensorFlow Training"
func init() {
goose.AddMigration(Up20200605090535, Down20200605090535)
func initialize20200605090535() {
if _, ok := initializedMigrations[20200605090535]; !ok {
goose.AddMigration(Up20200605090535, Down20200605090535)
initializedMigrations[20200605090535] = true
}
}
// Up20200605090535 will insert a tensorflow workflow template to each user.
@@ -101,6 +104,15 @@ func Up20200605090535(tx *sql.Tx) error {
return err
}
migrationsRan, err := getRanSQLMigrations(client)
if err != nil {
return err
}
if _, ok := migrationsRan[20200605090535]; ok {
return nil
}
namespaces, err := client.ListOnepanelEnabledNamespaces()
if err != nil {
return err

View File

@@ -5,7 +5,6 @@ import (
v1 "github.com/onepanelio/core/pkg"
uid2 "github.com/onepanelio/core/pkg/util/uid"
"github.com/pressly/goose"
"time"
)
const cvatWorkspaceTemplate2 = `# Docker containers that are part of the Workspace
@@ -119,21 +118,30 @@ routes:
# - -c
`
func init() {
goose.AddMigration(Up20200626113635, Down20200626113635)
func initialize20200626113635() {
if _, ok := initializedMigrations[20200626113635]; !ok {
goose.AddMigration(Up20200626113635, Down20200626113635)
initializedMigrations[20200626113635] = true
}
}
// Up20200626113635 updates the CVAT template to a new version.
func Up20200626113635(tx *sql.Tx) error {
// This code is executed when the migration is applied.
time.Sleep(2 * time.Second)
client, err := getClient()
if err != nil {
return err
}
migrationsRan, err := getRanSQLMigrations(client)
if err != nil {
return err
}
if _, ok := migrationsRan[20200626113635]; ok {
return nil
}
namespaces, err := client.ListOnepanelEnabledNamespaces()
if err != nil {
return err

View File

@@ -0,0 +1,175 @@
package migration
import (
"database/sql"
v1 "github.com/onepanelio/core/pkg"
uid2 "github.com/onepanelio/core/pkg/util/uid"
"github.com/pressly/goose"
)
const cvatWorkspaceTemplate3 = `# Docker containers that are part of the Workspace
containers:
- name: cvat-db
image: postgres:10-alpine
env:
- name: POSTGRES_USER
value: root
- name: POSTGRES_DB
value: cvat
- name: POSTGRES_HOST_AUTH_METHOD
value: trust
- name: PGDATA
value: /var/lib/psql/data
ports:
- containerPort: 5432
name: tcp
volumeMounts:
- name: db
mountPath: /var/lib/psql
- name: cvat-redis
image: redis:4.0-alpine
ports:
- containerPort: 6379
name: tcp
- name: cvat
image: onepanel/cvat:v0.7.10-stable
env:
- name: DJANGO_MODWSGI_EXTRA_ARGS
value: ""
- name: ALLOWED_HOSTS
value: '*'
- name: CVAT_REDIS_HOST
value: localhost
- name: CVAT_POSTGRES_HOST
value: localhost
- name: CVAT_SHARE_URL
value: /home/django/data
ports:
- containerPort: 8080
name: http
volumeMounts:
- name: data
mountPath: /home/django/data
- name: keys
mountPath: /home/django/keys
- name: logs
mountPath: /home/django/logs
- name: models
mountPath: /home/django/models
- name: share
mountPath: /home/django/share
- name: cvat-ui
image: onepanel/cvat-ui:v0.7.10-stable
ports:
- containerPort: 80
name: http
# Uncomment following lines to enable S3 FileSyncer
# Refer to https://docs.onepanel.ai/docs/getting-started/use-cases/computervision/annotation/cvat/cvat_quick_guide#setting-up-environment-variables
#- name: filesyncer
# image: onepanel/filesyncer:v0.0.4
# command: ['python3', 'main.py']
# volumeMounts:
# - name: share
# mountPath: /mnt/share
ports:
- name: cvat-ui
port: 80
protocol: TCP
targetPort: 80
- name: cvat
port: 8080
protocol: TCP
targetPort: 8080
routes:
- match:
- uri:
regex: /api/.*|/git/.*|/tensorflow/.*|/auto_annotation/.*|/analytics/.*|/static/.*|/admin/.*|/documentation/.*|/dextr/.*|/reid/.*
- queryParams:
id:
regex: \d+.*
route:
- destination:
port:
number: 8080
timeout: 600s
- match:
- uri:
prefix: /
route:
- destination:
port:
number: 80
timeout: 600s
# DAG Workflow to be executed once a Workspace action completes (optional)
# Uncomment the lines below if you want to send Slack notifications
#postExecutionWorkflow:
# entrypoint: main
# templates:
# - name: main
# dag:
# tasks:
# - name: slack-notify
# template: slack-notify
# - name: slack-notify
# container:
# image: technosophos/slack-notify
# args:
# - SLACK_USERNAME=onepanel SLACK_TITLE="Your workspace is ready" SLACK_ICON=https://www.gravatar.com/avatar/5c4478592fe00878f62f0027be59c1bd SLACK_MESSAGE="Your workspace is now running" ./slack-notify
# command:
# - sh
# - -c
`
func initialize20200704151301() {
if _, ok := initializedMigrations[20200704151301]; !ok {
goose.AddMigration(Up20200704151301, Down20200704151301)
initializedMigrations[20200704151301] = true
}
}
// Up20200704151301 updates the CVAT template to a new version.
func Up20200704151301(tx *sql.Tx) error {
// This code is executed when the migration is applied.
client, err := getClient()
if err != nil {
return err
}
migrationsRan, err := getRanSQLMigrations(client)
if err != nil {
return err
}
if _, ok := migrationsRan[20200704151301]; ok {
return nil
}
namespaces, err := client.ListOnepanelEnabledNamespaces()
if err != nil {
return err
}
uid, err := uid2.GenerateUID(cvatTemplateName, 30)
if err != nil {
return err
}
workspaceTemplate := &v1.WorkspaceTemplate{
UID: uid,
Name: cvatTemplateName,
Manifest: cvatWorkspaceTemplate3,
}
for _, namespace := range namespaces {
if _, err := client.UpdateWorkspaceTemplate(namespace.Name, workspaceTemplate); err != nil {
return err
}
}
return nil
}
// Down20200704151301 removes the CVAT template update
func Down20200704151301(tx *sql.Tx) error {
// This code is executed when the migration is rolled back.
return nil
}

58
db/go/db.go Normal file
View File

@@ -0,0 +1,58 @@
package migration
import (
sq "github.com/Masterminds/squirrel"
"github.com/jmoiron/sqlx"
v1 "github.com/onepanelio/core/pkg"
)
// initializedMigrations is used to keep track of which migrations have been initialized.
// if they are initialzed more than once, goose panics.
var initializedMigrations = make(map[int]bool)
// Initialize sets up the go migrations.
func Initialize() {
initialize20200525160514()
initialize20200528140124()
initialize20200605090509()
initialize20200605090535()
initialize20200626113635()
initialize20200704151301()
}
func getClient() (*v1.Client, error) {
kubeConfig := v1.NewConfig()
client, err := v1.NewClient(kubeConfig, nil, nil)
if err != nil {
return nil, err
}
config, err := client.GetSystemConfig()
if err != nil {
return nil, err
}
dbDriverName, dbDataSourceName := config.DatabaseConnection()
client.DB = v1.NewDB(sqlx.MustConnect(dbDriverName, dbDataSourceName))
return client, nil
}
// getRanSQLMigrations returns a map where each key is a sql migration version ran.
func getRanSQLMigrations(client *v1.Client) (map[uint64]bool, error) {
sb := sq.StatementBuilder.PlaceholderFormat(sq.Dollar)
query := sb.Select("version_id").
From("goose_db_version")
versions := make([]uint64, 0)
if err := client.Selectx(&versions, query); err != nil {
return nil, err
}
result := make(map[uint64]bool)
for _, version := range versions {
result[version] = true
}
return result, nil
}

View File

@@ -2,5 +2,4 @@
ALTER TABLE workflow_template_versions DROP COLUMN uid;
-- +goose Down
ALTER TABLE workflow_template_versions ADD COLUMN uid VARCHAR(30);
UPDATE workflow_template_versions SET uid = version::text;

View File

@@ -0,0 +1,11 @@
-- +goose Up
-- SQL in this section is executed when the migration is applied.
ALTER TABLE workflow_template_versions ALTER COLUMN version TYPE BIGINT;
ALTER TABLE workspace_template_versions ALTER COLUMN version TYPE BIGINT;
ALTER TABLE workspaces ALTER COLUMN workspace_template_version TYPE BIGINT;
-- +goose Down
-- SQL in this section is executed when the migration is rolled back.
ALTER TABLE workflow_template_versions ALTER COLUMN version TYPE INT;
ALTER TABLE workspace_template_versions ALTER COLUMN version TYPE INT;
ALTER TABLE workspaces ALTER COLUMN workspace_template_version TYPE INT;

8
go.mod
View File

@@ -4,6 +4,7 @@ go 1.14
require (
github.com/Azure/go-autorest v14.0.0+incompatible // indirect
cloud.google.com/go/storage v1.6.0
github.com/Azure/go-autorest/autorest/adal v0.8.2 // indirect
github.com/Masterminds/squirrel v1.1.0
github.com/argoproj/argo v0.0.0-20200331233432-4d1175eb68f6
@@ -14,11 +15,10 @@ require (
github.com/ghodss/yaml v1.0.0
github.com/go-sql-driver/mysql v1.5.0 // indirect
github.com/golang/protobuf v1.4.1
github.com/google/uuid v1.1.1
github.com/gorilla/handlers v1.4.2
github.com/grpc-ecosystem/go-grpc-middleware v1.2.0
github.com/grpc-ecosystem/grpc-gateway v1.14.4
github.com/hashicorp/go-uuid v1.0.2
github.com/hashicorp/go-uuid v1.0.2 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/jmoiron/sqlx v1.2.0
github.com/lib/pq v1.3.0
@@ -30,10 +30,14 @@ require (
github.com/spf13/cobra v0.0.5 // indirect
github.com/stretchr/testify v1.4.0
github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc
golang.org/x/net v0.0.0-20200301022130-244492dfa37a
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
google.golang.org/api v0.20.0
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84
google.golang.org/grpc v1.28.0
google.golang.org/protobuf v1.22.0
gopkg.in/yaml.v2 v2.2.8
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
istio.io/api v0.0.0-20200107183329-ed4b507c54e1
k8s.io/api v0.16.4
k8s.io/apimachinery v0.16.7-beta.0

24
go.sum
View File

@@ -12,14 +12,18 @@ cloud.google.com/go v0.55.0 h1:eoz/lYxKSL4CNAiaUJ0ZfD1J3bfMYbU5B3rwM1C1EIU=
cloud.google.com/go v0.55.0/go.mod h1:ZHmoY+/lIMNkN2+fBmuTiqZ4inFhvQad8ft7MT8IV5Y=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0 h1:xE3CPsOgttP4ACBePh79zTKALtXwn/Edhcr16R5hMWU=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0 h1:Lpy6hKgdcl7a3WGSfJIFmxmcdjSpP6OmBEfcOv1Y680=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0 h1:UDpwYIwla4jHGzZJaEJYx1tOejbgSoNqsAfHAUYe2r8=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/go-autorest v11.1.2+incompatible h1:viZ3tV5l4gE2Sw0xrasFHytCGtzYCrT+um/rrSQ1BfA=
@@ -31,8 +35,6 @@ github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+B
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
github.com/Azure/go-autorest/autorest/adal v0.8.2 h1:O1X4oexUxnZCaEUGsvMnr8ZGj8HI37tNezwY4npRqA0=
github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
github.com/Azure/go-autorest/autorest/adal v0.8.3 h1:O1AGG9Xig71FxdX9HO5pGNyZ7TbSyHaVg+5eJO/jSGw=
github.com/Azure/go-autorest/autorest/adal v0.8.3/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM=
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
@@ -44,6 +46,7 @@ github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1Gn
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
@@ -207,6 +210,7 @@ github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSN
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@@ -218,6 +222,7 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk=
@@ -237,8 +242,6 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xC
github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 h1:0IKlLyQ3Hs9nDaiK5cSHAGmcQEIC8l2Ts1u6x5Dfrqg=
github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s=
github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c=
github.com/grpc-ecosystem/grpc-gateway v1.14.3 h1:OCJlWkOUoTnl0neNGlf4fUm3TmbEtguw7vR+nGtnDjY=
github.com/grpc-ecosystem/grpc-gateway v1.14.3/go.mod h1:6CwZWGDSPRJidgKAtJVvND6soZe6fT7iteq8wDPdhb0=
github.com/grpc-ecosystem/grpc-gateway v1.14.4 h1:IOPK2xMPP3aV6/NPt4jt//ELFo3Vv8sDVD8j3+tleDU=
github.com/grpc-ecosystem/grpc-gateway v1.14.4/go.mod h1:6CwZWGDSPRJidgKAtJVvND6soZe6fT7iteq8wDPdhb0=
github.com/hashicorp/go-uuid v0.0.0-20180228145832-27454136f036/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
@@ -275,6 +278,7 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
@@ -437,6 +441,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
@@ -479,6 +484,7 @@ golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHl
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
@@ -486,6 +492,7 @@ golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKG
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -527,6 +534,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -608,6 +616,7 @@ golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200317043434-63da46f3035e/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200331202046-9d5940d49312 h1:2PHG+Ia3gK1K2kjxZnSylizb//eyaMG8gDFbOG7wLV8=
golang.org/x/tools v0.0.0-20200331202046-9d5940d49312/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -622,6 +631,7 @@ google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0 h1:jz2KixHX7EcCPiQrySzPdnYT7DbINAypCqKZ1Z7GM40=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -648,8 +658,6 @@ google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200317114155-1f3552e48f24/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200413115906-b5235f65be36 h1:j7CmVRD4Kec0+f8VuBAc2Ak2MFfXm5Q2/RxuJLL+76E=
google.golang.org/genproto v0.0.0-20200413115906-b5235f65be36/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84 h1:pSLkPbrjnPyLDYUO2VM9mDLqo2V6CFBY84lFSZAfoi4=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@@ -711,11 +719,14 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
istio.io/api v0.0.0-20200107183329-ed4b507c54e1 h1:q4xggEkhMn4RMRo8AJVNmMtNzy514DGiAUxRDDKPhZU=
istio.io/api v0.0.0-20200107183329-ed4b507c54e1/go.mod h1:+cyHH83OwC0rFpwk8eXctzPNpiCAbB+r6kmMiAxxBHw=
@@ -730,7 +741,6 @@ k8s.io/apimachinery v0.0.0-20191219145857-f69eda767ee8/go.mod h1:mhhO3hoLkWO+2eC
k8s.io/apimachinery v0.16.4/go.mod h1:llRdnznGEAqC3DcNm6yEj472xaFVfLM7hnYofMb12tQ=
k8s.io/apimachinery v0.16.7-beta.0 h1:1cNiN7ZXJzlWq7dnWojG5UcrX1AIfQqpbyuzhu7Bhsc=
k8s.io/apimachinery v0.16.7-beta.0/go.mod h1:mhhO3hoLkWO+2eCvqjPtH2Ly92l9nJDwsswzWKpkN2w=
k8s.io/apimachinery v0.18.3 h1:pOGcbVAhxADgUYnjS08EFXs9QMl8qaH5U4fr5LGUrSk=
k8s.io/client-go v0.0.0-20191225075139-73fd2ddc9180/go.mod h1:ksVkYlACXo9hR9AV+cYyCkuWL1xnWcGtAFxsfqMcozg=
k8s.io/client-go v0.16.4 h1:sf+FEZXYhJNjpTZapQDLvvN+0kBeUTxCYxlXcVdhv2E=
k8s.io/client-go v0.16.4/go.mod h1:ZgxhFDxSnoKY0J0U2/Y1C8obKDdlhGPZwA7oHH863Ok=

37
main.go
View File

@@ -4,7 +4,6 @@ import (
"context"
"flag"
"fmt"
_ "github.com/onepanelio/core/db"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
corev1 "k8s.io/api/core/v1"
@@ -15,6 +14,8 @@ import (
"k8s.io/client-go/tools/cache"
"net"
"net/http"
"path/filepath"
"strings"
"github.com/gorilla/handlers"
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
@@ -23,6 +24,7 @@ import (
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/jmoiron/sqlx"
"github.com/onepanelio/core/api"
migrations "github.com/onepanelio/core/db/go"
v1 "github.com/onepanelio/core/pkg"
"github.com/onepanelio/core/pkg/util/env"
"github.com/onepanelio/core/server"
@@ -67,16 +69,20 @@ func main() {
log.Fatalf("Failed to get system config: %v", err)
}
databaseDataSourceName := fmt.Sprintf("host=%v user=%v password=%v dbname=%v sslmode=disable",
sysConfig["databaseHost"], sysConfig["databaseUsername"], sysConfig["databasePassword"], sysConfig["databaseName"])
dbDriverName, databaseDataSourceName := sysConfig.DatabaseConnection()
// sqlx.MustConnect will panic when it can't connect to DB. In that case, this whole application will crash.
// This is okay, as the pod will restart and try connecting to DB again.
// dbDriverName may be nil, but sqlx will then panic.
dbDriverName := sysConfig.DatabaseDriverName()
db := sqlx.MustConnect(*dbDriverName, databaseDataSourceName)
if err := goose.Run("up", db.DB, "db"); err != nil {
log.Fatalf("Failed to run database migrations: %v", err)
db := sqlx.MustConnect(dbDriverName, databaseDataSourceName)
goose.SetTableName("goose_db_version")
if err := goose.Run("up", db.DB, filepath.Join("db", "sql")); err != nil {
log.Fatalf("Failed to run database sql migrations: %v", err)
}
goose.SetTableName("goose_db_go_version")
migrations.Initialize()
if err := goose.Run("up", db.DB, filepath.Join("db", "go")); err != nil {
log.Fatalf("Failed to run database go migrations: %v", err)
}
s := startRPCServer(v1.NewDB(db), kubeConfig, sysConfig, stopCh)
@@ -154,7 +160,7 @@ func startHTTPProxy() {
// Register gRPC server endpoint
// Note: Make sure the gRPC server is running properly and accessible
mux := runtime.NewServeMux()
mux := runtime.NewServeMux(runtime.WithIncomingHeaderMatcher(customHeaderMatcher))
opts := []grpc.DialOption{grpc.WithInsecure()}
registerHandler(api.RegisterWorkflowTemplateServiceHandlerFromEndpoint, ctx, mux, endpoint, opts)
@@ -246,3 +252,16 @@ func watchConfigmapChanges(client *v1.Client, namespace string, stopCh <-chan st
neverStopCh := make(chan struct{})
controller.Run(neverStopCh)
}
// customHeaderMatcher is used to allow certain headers so we don't require a grpc-gateway prefix
func customHeaderMatcher(key string) (string, bool) {
lowerCaseKey := strings.ToLower(key)
switch lowerCaseKey {
case "onepanel-auth-token":
return lowerCaseKey, true
case "cookie":
return lowerCaseKey, true
default:
return runtime.DefaultHeaderMatcher(key)
}
}

View File

@@ -3,6 +3,7 @@ package v1
import (
sq "github.com/Masterminds/squirrel"
argoprojv1alpha1 "github.com/argoproj/argo/pkg/client/clientset/versioned/typed/workflow/v1alpha1"
"github.com/onepanelio/core/pkg/util/gcs"
"github.com/onepanelio/core/pkg/util/s3"
log "github.com/sirupsen/logrus"
"k8s.io/client-go/kubernetes"
@@ -64,7 +65,8 @@ func NewClient(config *Config, db *DB, systemConfig SystemConfig) (client *Clien
}, nil
}
func (c *Client) GetS3Client(namespace string, config *ArtifactRepositoryS3Config) (s3Client *s3.Client, err error) {
// GetS3Client initializes a client to Amazon Cloud Storage.
func (c *Client) GetS3Client(namespace string, config *ArtifactRepositoryS3Provider) (s3Client *s3.Client, err error) {
s3Client, err = s3.NewClient(s3.Config{
Endpoint: config.Endpoint,
Region: config.Region,
@@ -80,6 +82,10 @@ func (c *Client) GetS3Client(namespace string, config *ArtifactRepositoryS3Confi
}).Error("getS3Client failed when initializing a new S3 client.")
return
}
return
}
// GetGCSClient initializes a client to Google Cloud Storage.
func (c *Client) GetGCSClient(namespace string, config *ArtifactRepositoryGCSProvider) (gcsClient *gcs.Client, err error) {
return gcs.NewClient(namespace, config.ServiceAccountJSON)
}

View File

@@ -1,10 +1,18 @@
package v1
import (
"flag"
"fmt"
argoFake "github.com/argoproj/argo/pkg/client/clientset/versioned/fake"
"github.com/jmoiron/sqlx"
"github.com/pressly/goose"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/fake"
"log"
"os"
"testing"
)
var (
@@ -15,6 +23,20 @@ var (
},
}
configArtifactRepository = `archiveLogs: true
s3:
keyFormat: artifacts/{{workflow.namespace}}/{{workflow.name}}/{{pod.name}}
bucket: test.onepanel.io
endpoint: s3.amazonaws.com
insecure: false
region: us-west-2
accessKeySecret:
name: onepanel
key: artifactRepositoryS3AccessKey
secretKeySecret:
name: onepanel
key: artifactRepositoryS3SecretKey`
mockSystemConfigMap = &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "onepanel",
@@ -22,6 +44,8 @@ var (
},
Data: map[string]string{
"ONEPANEL_HOST": "demo.onepanel.site",
"ONEPANEL_DOMAIN": "demo.onepanel.site",
"artifactRepository": configArtifactRepository,
"applicationNodePoolLabel": "beta.kubernetes.io/instance-type",
"applicationNodePoolOptions": `
- name: 'CPU: 2, RAM: 8GB'
@@ -34,8 +58,60 @@ var (
`,
},
}
database *sqlx.DB
)
func NewTestClient(objects ...runtime.Object) (client *Client) {
return &Client{Interface: fake.NewSimpleClientset(objects...)}
var flagDatabaseService = flag.String("db", "localhost", "Name to connect to db, defaults to localhost")
func TestMain(m *testing.M) {
// call flag.Parse() here if TestMain uses flags
flag.Parse()
databaseDataSourceName := fmt.Sprintf("host=%v user=%v password=%v dbname=%v sslmode=disable",
*flagDatabaseService, "admin", "tester", "onepanel")
dbDriverName := "postgres"
database = sqlx.MustConnect(dbDriverName, databaseDataSourceName)
// We don't run the go migrations as those setup data that we don't use in our testing
if err := goose.Run("up", database.DB, "../db/sql"); err != nil {
log.Fatalf("Failed to run database migrations: %v", err)
}
os.Exit(m.Run())
}
func NewTestClient(db *sqlx.DB, objects ...runtime.Object) (client *Client) {
k8sFake := fake.NewSimpleClientset(objects...)
argoFakeClient := argoFake.NewSimpleClientset()
return &Client{
Interface: k8sFake,
DB: NewDB(db),
argoprojV1alpha1: argoFakeClient.ArgoprojV1alpha1(),
}
}
func DefaultTestClient() *Client {
return NewTestClient(database, mockSystemConfigMap, mockSystemSecret)
}
func clearDatabase(t *testing.T) {
// We do not delete from goose_db_version as we need it to mark the migrations as ran.
query := `
DELETE FROM labels;
DELETE FROM workspaces;
DELETE FROM workflow_executions;
DELETE FROM cron_workflows;
DELETE FROM workspace_templates;
DELETE FROM workflow_templates;
DELETE FROM workspace_template_versions;
DELETE FROM workflow_template_versions;
`
_, err := database.Exec(query)
if err != nil {
t.Fatal(err)
}
}

View File

@@ -2,92 +2,13 @@ package v1
import (
"encoding/base64"
"fmt"
"github.com/onepanelio/core/pkg/util"
"github.com/onepanelio/core/pkg/util/ptr"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc/codes"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/yaml"
"strings"
)
// SystemConfig is configuration loaded from kubernetes config and secrets that includes information about the
// database, server, etc.
type SystemConfig map[string]string
// GetValue returns the value in the underlying map if it exists, otherwise nil is returned
// If the value does not exist, it is also logged.
func (s SystemConfig) GetValue(name string) *string {
value, ok := s[name]
if !ok {
log.WithFields(log.Fields{
"Method": "SystemConfig.GetValue",
"Name": name,
"Error": "does not exist",
})
return nil
}
return &value
}
// Domain gets the ONEPANEL_DOMAIN value, or nil.
func (s SystemConfig) Domain() *string {
return s.GetValue("ONEPANEL_DOMAIN")
}
// APIURL gets the ONEPANEL_API_URL, or nil.
func (s SystemConfig) APIURL() *string {
return s.GetValue("ONEPANEL_API_URL")
}
// APIProtocol returns either http:// or https:// or nil.
// It is based on the ONEPANEL_API_URL config value and checks if it has https or http
func (s SystemConfig) APIProtocol() *string {
url := s.APIURL()
if url == nil {
return nil
}
if strings.HasPrefix(*url, "https://") {
return ptr.String("https://")
}
return ptr.String("http://")
}
// FQDN gets the ONEPANEL_FQDN value or nil.
func (s SystemConfig) FQDN() *string {
return s.GetValue("ONEPANEL_FQDN")
}
// NodePoolLabel gets the applicationNodePoolLabel from the config or returns nil.
func (s SystemConfig) NodePoolLabel() (label *string) {
return s.GetValue("applicationNodePoolLabel")
}
// NodePoolOptions loads and parses the applicationNodePoolOptions from the config.
// If there is no data, an error is returned.
func (s SystemConfig) NodePoolOptions() (options []*ParameterOption, err error) {
data := s.GetValue("applicationNodePoolOptions")
if data == nil {
return nil, fmt.Errorf("no nodePoolOptions in config")
}
if err = yaml.Unmarshal([]byte(*data), &options); err != nil {
return
}
return
}
// DatabaseDriverName gets the databaseDriverName value, or nil.
func (s SystemConfig) DatabaseDriverName() *string {
return s.GetValue("databaseDriverName")
}
func (c *Client) getConfigMap(namespace, name string) (configMap *ConfigMap, err error) {
cm, err := c.CoreV1().ConfigMaps(namespace).Get(name, metav1.GetOptions{})
if err != nil {
@@ -115,20 +36,19 @@ func (c *Client) GetSystemConfig() (config SystemConfig, err error) {
}
namespace := "onepanel"
configMap, err := c.getConfigMap(namespace, "onepanel")
if err != nil {
return
}
config = configMap.Data
name := "onepanel"
secret, err := c.GetSecret(namespace, "onepanel")
configMap, err := c.getConfigMap(namespace, name)
if err != nil {
return
}
databaseUsername, _ := base64.StdEncoding.DecodeString(secret.Data["databaseUsername"])
config["databaseUsername"] = string(databaseUsername)
databasePassword, _ := base64.StdEncoding.DecodeString(secret.Data["databasePassword"])
config["databasePassword"] = string(databasePassword)
secret, err := c.GetSecret(namespace, name)
if err != nil {
return
}
config, err = NewSystemConfig(configMap, secret)
c.systemConfig = config
@@ -152,11 +72,11 @@ func (c *Client) GetNamespaceConfig(namespace string) (config *NamespaceConfig,
return
}
config = &NamespaceConfig{
ArtifactRepository: ArtifactRepositoryConfig{},
ArtifactRepository: ArtifactRepositoryProvider{},
}
err = yaml.Unmarshal([]byte(configMap.Data["artifactRepository"]), &config.ArtifactRepository)
if err != nil || config.ArtifactRepository.S3 == nil {
if err != nil || (config.ArtifactRepository.S3 == nil && config.ArtifactRepository.GCS == nil) {
return nil, util.NewUserError(codes.NotFound, "Artifact repository config not found.")
}
@@ -169,14 +89,22 @@ func (c *Client) GetNamespaceConfig(namespace string) (config *NamespaceConfig,
return
}
// TODO: replace with switch statement to support additional object storage
if config.ArtifactRepository.S3 == nil {
switch {
case config.ArtifactRepository.S3 != nil:
{
accessKey, _ := base64.StdEncoding.DecodeString(secret.Data[config.ArtifactRepository.S3.AccessKeySecret.Key])
config.ArtifactRepository.S3.AccessKey = string(accessKey)
secretKey, _ := base64.StdEncoding.DecodeString(secret.Data[config.ArtifactRepository.S3.SecretKeySecret.Key])
config.ArtifactRepository.S3.Secretkey = string(secretKey)
}
case config.ArtifactRepository.GCS != nil:
{
serviceJSON, _ := base64.StdEncoding.DecodeString(secret.Data[config.ArtifactRepository.GCS.ServiceAccountKeySecret.Key])
config.ArtifactRepository.GCS.ServiceAccountJSON = string(serviceJSON)
}
default:
return nil, util.NewUserError(codes.NotFound, "Artifact repository config not found.")
}
accessKey, _ := base64.StdEncoding.DecodeString(secret.Data[config.ArtifactRepository.S3.AccessKeySecret.Key])
config.ArtifactRepository.S3.AccessKey = string(accessKey)
secretKey, _ := base64.StdEncoding.DecodeString(secret.Data[config.ArtifactRepository.S3.SecretKeySecret.Key])
config.ArtifactRepository.S3.Secretkey = string(secretKey)
return
}

View File

@@ -1,27 +1,299 @@
package v1
import (
corev1 "k8s.io/api/core/v1"
"encoding/base64"
"fmt"
"strings"
"github.com/onepanelio/core/pkg/util/ptr"
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v3"
corev1 "k8s.io/api/core/v1"
k8yaml "sigs.k8s.io/yaml"
)
type ArtifactRepositoryS3Config struct {
KeyFormat string
// SystemConfig is configuration loaded from kubernetes config and secrets that includes information about the
// database, server, etc.
type SystemConfig map[string]string
// NodePoolOption extends ParameterOption to support resourceRequirements
type NodePoolOption struct {
ParameterOption
Resources corev1.ResourceRequirements
}
// NewSystemConfig creates a System config by getting the required data from a ConfigMap and Secret
func NewSystemConfig(configMap *ConfigMap, secret *Secret) (config SystemConfig, err error) {
config = configMap.Data
databaseUsername, err := base64.StdEncoding.DecodeString(secret.Data["databaseUsername"])
if err != nil {
return
}
config["databaseUsername"] = string(databaseUsername)
databasePassword, err := base64.StdEncoding.DecodeString(secret.Data["databasePassword"])
if err != nil {
return
}
config["databasePassword"] = string(databasePassword)
return
}
// GetValue returns the value in the underlying map if it exists, otherwise nil is returned
// If the value does not exist, it is also logged.
func (s SystemConfig) GetValue(name string) *string {
value, ok := s[name]
if !ok {
log.WithFields(log.Fields{
"Method": "SystemConfig.GetValue",
"Name": name,
"Error": "does not exist",
})
return nil
}
return &value
}
// Domain gets the ONEPANEL_DOMAIN value, or nil.
func (s SystemConfig) Domain() *string {
return s.GetValue("ONEPANEL_DOMAIN")
}
// APIURL gets the ONEPANEL_API_URL, or nil.
func (s SystemConfig) APIURL() *string {
return s.GetValue("ONEPANEL_API_URL")
}
// APIProtocol returns either http:// or https:// or nil.
// It is based on the ONEPANEL_API_URL config value and checks if it has https or http
func (s SystemConfig) APIProtocol() *string {
url := s.APIURL()
if url == nil {
return nil
}
if strings.HasPrefix(*url, "https://") {
return ptr.String("https://")
}
return ptr.String("http://")
}
// FQDN gets the ONEPANEL_FQDN value or nil.
func (s SystemConfig) FQDN() *string {
return s.GetValue("ONEPANEL_FQDN")
}
// NodePoolLabel gets the applicationNodePoolLabel from the config or returns nil.
func (s SystemConfig) NodePoolLabel() (label *string) {
return s.GetValue("applicationNodePoolLabel")
}
// NodePoolOptions loads and parses the applicationNodePoolOptions from the config.
// If there is no data, an error is returned.
func (s SystemConfig) NodePoolOptions() (options []*NodePoolOption, err error) {
data := s.GetValue("applicationNodePoolOptions")
if data == nil {
return nil, fmt.Errorf("no nodePoolOptions in config")
}
if err = k8yaml.Unmarshal([]byte(*data), &options); err != nil {
return
}
return
}
// NodePoolOptionByValue returns the nodePoolOption based on a given value
func (s SystemConfig) NodePoolOptionByValue(value string) (option *NodePoolOption, err error) {
options, err := s.NodePoolOptions()
if err != nil {
return
}
for _, opt := range options {
if opt.Value == value {
option = opt
return
}
}
return
}
// DatabaseDriverName gets the databaseDriverName value, or nil.
func (s SystemConfig) DatabaseDriverName() *string {
return s.GetValue("databaseDriverName")
}
// DatabaseConnection returns system config information to connect to a database
func (s SystemConfig) DatabaseConnection() (driverName, dataSourceName string) {
dataSourceName = fmt.Sprintf("host=%v user=%v password=%v dbname=%v sslmode=disable",
s["databaseHost"], s["databaseUsername"], s["databasePassword"], s["databaseName"])
driverName = *s.DatabaseDriverName()
return
}
// UpdateNodePoolOptions will update the sys-node-pool parameter's options with runtime values
// The original slice is unmodified, the returned slice has the updated values
// If sys-node-pool is not present, nothing happens.
func (s SystemConfig) UpdateNodePoolOptions(parameters []Parameter) ([]Parameter, error) {
result := make([]Parameter, 0)
var nodePoolParameter *Parameter
// Copy the original parameters, skipping sys-node-pool
for i := range parameters {
parameter := parameters[i]
if parameter.Name == "sys-node-pool" {
nodePoolParameter = &parameter
continue
}
result = append(result, parameter)
}
if nodePoolParameter == nil {
return result, nil
}
nodePoolOptions, err := s.NodePoolOptions()
if err != nil {
return result, err
}
options := make([]*ParameterOption, 0)
for _, option := range nodePoolOptions {
newOption := &ParameterOption{
Name: option.Name,
Value: option.Value,
}
options = append(options, newOption)
}
nodePoolParameter.Options = options
result = append(result, *nodePoolParameter)
return result, nil
}
// ArtifactRepositoryS3Provider is meant to be used
// by the CLI. CLI will marshal this struct into the correct
// YAML structure for k8s configmap / secret.
type ArtifactRepositoryS3Provider struct {
KeyFormat string `yaml:"keyFormat"`
Bucket string
Endpoint string
Insecure bool
Region string
AccessKeySecret corev1.SecretKeySelector
SecretKeySecret corev1.SecretKeySelector
AccessKey string
Secretkey string
AccessKeySecret ArtifactRepositorySecret `yaml:"accessKeySecret"`
SecretKeySecret ArtifactRepositorySecret `yaml:"secretKeySecret"`
AccessKey string `yaml:"accessKey,omitempty"`
Secretkey string `yaml:"secretKey,omitempty"`
}
// ArtifactRepositoryGCSProvider is meant to be used
// by the CLI. CLI will marshal this struct into the correct
// YAML structure for k8s configmap / secret.
type ArtifactRepositoryGCSProvider struct {
KeyFormat string `yaml:"keyFormat"`
Bucket string
Endpoint string
Insecure bool
ServiceAccountKey string `yaml:"serviceAccountKey,omitempty"`
ServiceAccountKeySecret ArtifactRepositorySecret `yaml:"serviceAccountKeySecret"`
ServiceAccountJSON string `yaml:"serviceAccountJSON,omitempty"`
}
// ArtifactRepositoryProvider is used to setup access into AWS Cloud Storage
// or Google Cloud storage.
// - The relevant sub-struct (S3, GCS) is unmarshalled into from the cluster configmap.
// Right now, either the S3 or GCS struct will be filled in. Multiple cloud
// providers are not supported at the same time in params.yaml (manifests deployment).
type ArtifactRepositoryProvider struct {
S3 *ArtifactRepositoryS3Provider `yaml:"s3,omitempty"`
GCS *ArtifactRepositoryGCSProvider `yaml:"gcs,omitempty"`
}
// ArtifactRepositorySecret holds information about a kubernetes Secret.
// - The "key" is the specific key inside the Secret.
// - The "name" is the name of the Secret.
// Usually, this is used to figure out what secret to look into for a specific value.
type ArtifactRepositorySecret struct {
Key string `yaml:"key"`
Name string `yaml:"name"`
}
// MarshalToYaml is used by the CLI to generate configmaps during deployment
// or build operations.
func (a *ArtifactRepositoryS3Provider) MarshalToYaml() (string, error) {
builder := &strings.Builder{}
encoder := yaml.NewEncoder(builder)
encoder.SetIndent(6)
defer encoder.Close()
err := encoder.Encode(&ArtifactRepositoryProvider{
S3: &ArtifactRepositoryS3Provider{
KeyFormat: a.KeyFormat,
Bucket: a.Bucket,
Endpoint: a.Endpoint,
Insecure: a.Insecure,
Region: a.Region,
AccessKeySecret: ArtifactRepositorySecret{
Name: a.AccessKeySecret.Name,
Key: a.AccessKeySecret.Key,
},
SecretKeySecret: ArtifactRepositorySecret{
Name: a.SecretKeySecret.Name,
Key: a.SecretKeySecret.Key,
},
},
})
if err != nil {
return "", err
}
return builder.String(), nil
}
// MarshalToYaml is used by the CLI to generate configmaps during deployment
// or build operations.
func (g *ArtifactRepositoryGCSProvider) MarshalToYaml() (string, error) {
builder := &strings.Builder{}
encoder := yaml.NewEncoder(builder)
encoder.SetIndent(6)
defer encoder.Close()
err := encoder.Encode(&ArtifactRepositoryProvider{
GCS: &ArtifactRepositoryGCSProvider{
KeyFormat: g.KeyFormat,
Bucket: g.Bucket,
Endpoint: g.Endpoint,
Insecure: g.Insecure,
ServiceAccountKeySecret: ArtifactRepositorySecret{
Key: "artifactRepositoryGCSServiceAccountKey",
Name: "onepanel",
},
},
})
if err != nil {
return "", err
}
return builder.String(), nil
}
// FormatKey replaces placeholder values with their actual values and returns this string.
// {{workflow.namespace}} -> namespace
// {{workflow.name}} -> workflowName
// {{pod.name}} -> podName
func (a *ArtifactRepositoryS3Config) FormatKey(namespace, workflowName, podName string) string {
func (a *ArtifactRepositoryS3Provider) FormatKey(namespace, workflowName, podName string) string {
keyFormat := a.KeyFormat
keyFormat = strings.Replace(keyFormat, "{{workflow.namespace}}", namespace, -1)
@@ -31,10 +303,20 @@ func (a *ArtifactRepositoryS3Config) FormatKey(namespace, workflowName, podName
return keyFormat
}
type ArtifactRepositoryConfig struct {
S3 *ArtifactRepositoryS3Config
// FormatKey replaces placeholder values with their actual values and returns this string.
// {{workflow.namespace}} -> namespace
// {{workflow.name}} -> workflowName
// {{pod.name}} -> podName
func (g *ArtifactRepositoryGCSProvider) FormatKey(namespace, workflowName, podName string) string {
keyFormat := g.KeyFormat
keyFormat = strings.Replace(keyFormat, "{{workflow.namespace}}", namespace, -1)
keyFormat = strings.Replace(keyFormat, "{{workflow.name}}", workflowName, -1)
keyFormat = strings.Replace(keyFormat, "{{pod.name}}", podName, -1)
return keyFormat
}
type NamespaceConfig struct {
ArtifactRepository ArtifactRepositoryConfig
ArtifactRepository ArtifactRepositoryProvider
}

View File

@@ -21,8 +21,9 @@ func testCreateNamespace(c *Client) {
})
}
}
func TestListNamespace(t *testing.T) {
c := NewTestClient()
func TestClient_ListNamespace(t *testing.T) {
c := DefaultTestClient()
testCreateNamespace(c)

View File

@@ -6,8 +6,8 @@ import (
"github.com/stretchr/testify/assert"
)
func TestCreateSecret(t *testing.T) {
c := NewTestClient()
func TestClient_CreateSecret(t *testing.T) {
c := DefaultTestClient()
err := c.CreateSecret("namespace", &Secret{
Name: "name",
@@ -15,8 +15,8 @@ func TestCreateSecret(t *testing.T) {
assert.Nil(t, err)
}
func TestGetSecret(t *testing.T) {
c := NewTestClient()
func TestClient_GetSecret(t *testing.T) {
c := DefaultTestClient()
err := c.CreateSecret("namespace", &Secret{
Name: "name",

57
pkg/util/gcs/gcs.go Normal file
View File

@@ -0,0 +1,57 @@
package gcs
import (
"cloud.google.com/go/storage"
log "github.com/sirupsen/logrus"
"golang.org/x/net/context"
"golang.org/x/oauth2/google"
"google.golang.org/api/option"
"io"
)
// Client is a struct used for accessing Google Cloud Storage.
type Client struct {
*storage.Client
}
// NewClient handles the details of initializing the connection to Google Cloud Storage.
// - Note that the permissions are set to ReadWrite.
func NewClient(namespace string, serviceAccountJSON string) (gcsClient *Client, err error) {
ctx := context.Background()
creds, err := google.CredentialsFromJSON(ctx, []byte(serviceAccountJSON), storage.ScopeReadWrite)
if err != nil {
log.WithFields(log.Fields{
"Namespace": namespace,
"JSON": serviceAccountJSON,
"Error": err.Error(),
}).Error("GetGCSClient failed when initializing a new GCS client.")
return
}
client, err := storage.NewClient(ctx, option.WithCredentials(creds))
if err != nil {
log.WithFields(log.Fields{
"Namespace": namespace,
"JSON": serviceAccountJSON,
"Error": err.Error(),
}).Error("GetGCSClient failed when initializing a new GCS client.")
return
}
return &Client{Client: client}, nil
}
/* GetObject retrieves a specific object from Google Cloud Storage.
- Function Name is meant to be consistent with S3's.
*/
func (c *Client) GetObject(bucket, key string) (stream io.ReadCloser, err error) {
ctx := context.Background()
stream, err = c.Client.Bucket(bucket).Object(key).NewReader(ctx)
if err != nil {
return
}
if stream == nil {
defer stream.Close()
}
return
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,99 @@
package v1
import (
"github.com/stretchr/testify/assert"
"testing"
)
// TestClient_CreateWorkflowExecution tests creating a workflow execution
func TestClient_CreateWorkflowExecution(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
namespace := "onepanel"
wt := &WorkflowTemplate{
Name: "test",
Manifest: defaultWorkflowTemplate,
}
wt, _ = c.CreateWorkflowTemplate(namespace, wt)
we := &WorkflowExecution{
Name: "test",
}
we, err := c.CreateWorkflowExecution(namespace, we, wt)
assert.Nil(t, err)
}
// TestClient_GetWorkflowExecution tests getting a workflow execution that exists
func TestClient_GetWorkflowExecution(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
namespace := "onepanel"
wt := &WorkflowTemplate{
Name: "test",
Manifest: defaultWorkflowTemplate,
}
wt, _ = c.CreateWorkflowTemplate(namespace, wt)
we := &WorkflowExecution{
Name: "test",
}
we, _ = c.CreateWorkflowExecution(namespace, we, wt)
getWe, err := c.GetWorkflowExecution(namespace, we.UID)
assert.Nil(t, err)
assert.Equal(t, we.Name, getWe.Name)
assert.Equal(t, we.UID, getWe.UID)
}
// TestClient_GetWorkflowExecution tests getting a workflow execution that doesn't exist
func TestClient_GetWorkflowExecution_NotExists(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
namespace := "onepanel"
getWe, err := c.GetWorkflowExecution(namespace, "not-exist")
assert.Nil(t, getWe)
assert.Nil(t, err)
}
// TestClient_ArchiveWorkflowExecution_NotExist makes sure there is no error if the workflow
// execution does not exist
func TestClient_ArchiveWorkflowExecution_NotExist(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
err := c.ArchiveWorkflowExecution("onepanel-no-exist", "test-no-exist")
assert.Nil(t, err)
}
// TestClient_ArchiveWorkflowExecution_Exist makes sure we archive an existing workflow execution correctly
func TestClient_ArchiveWorkflowExecution_Exist(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
namespace := "onepanel"
weName := "test"
wt := &WorkflowTemplate{
Name: "test",
Manifest: defaultWorkflowTemplate,
}
wt, _ = c.CreateWorkflowTemplate(namespace, wt)
we := &WorkflowExecution{
Name: weName,
}
we, err := c.CreateWorkflowExecution(namespace, we, wt)
err = c.ArchiveWorkflowExecution(namespace, weName)
assert.Nil(t, err)
}

View File

@@ -3,6 +3,7 @@ package v1
import (
"encoding/json"
wfv1 "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1"
uid2 "github.com/onepanelio/core/pkg/util/uid"
"github.com/onepanelio/core/util/sql"
"time"
)
@@ -22,6 +23,7 @@ type WorkflowExecution struct {
FinishedAt *time.Time `db:"finished_at"`
WorkflowTemplate *WorkflowTemplate `db:"workflow_template"`
Labels map[string]string
ArgoWorkflow *wfv1.Workflow
}
// WorkflowExecutionOptions are options you have for an executing workflow
@@ -55,6 +57,18 @@ type WorkflowExecutionStatus struct {
FinishedAt *time.Time `db:"finished_at" json:"finishedAt"`
}
// GenerateUID generates a uid from the input name and sets it on the workflow execution
func (we *WorkflowExecution) GenerateUID(name string) error {
result, err := uid2.GenerateUID(name, 63)
if err != nil {
return err
}
we.UID = result
return nil
}
// LoadParametersFromBytes loads Parameters from the WorkflowExecution's ParameterBytes field.
func (we *WorkflowExecution) LoadParametersFromBytes() ([]Parameter, error) {
loadedParameters := make([]Parameter, 0)
@@ -75,6 +89,17 @@ func (we *WorkflowExecution) LoadParametersFromBytes() ([]Parameter, error) {
return we.Parameters, err
}
// GetParameterValue returns the value of the parameter with the given name, or nil if there is no such parameter
func (we *WorkflowExecution) GetParameterValue(name string) *string {
for _, p := range we.Parameters {
if p.Name == name {
return p.Value
}
}
return nil
}
// getWorkflowExecutionColumns returns all of the columns for workflowExecution modified by alias, destination.
// see formatColumnSelect
func getWorkflowExecutionColumns(aliasAndDestination ...string) []string {

View File

@@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"github.com/onepanelio/core/pkg/util/pagination"
uid2 "github.com/onepanelio/core/pkg/util/uid"
"strconv"
"strings"
"time"
@@ -21,23 +20,67 @@ import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func (c *Client) createWorkflowTemplate(namespace string, workflowTemplate *WorkflowTemplate) (*WorkflowTemplate, *WorkflowTemplateVersion, error) {
uid, err := uid2.GenerateUID(workflowTemplate.Name, 30)
// createWorkflowTemplateVersionDB inserts a record into workflow_template_versions using the current time accurate to nanoseconds
// the data is returned in the resulting WorkflowTemplateVersion struct.
func createWorkflowTemplateVersionDB(runner sq.BaseRunner, workflowTemplateID uint64, manifest string, latest bool) (workflowTemplateVersion *WorkflowTemplateVersion, err error) {
ts := time.Now().UnixNano()
workflowTemplateVersion = &WorkflowTemplateVersion{
WorkflowTemplate: &WorkflowTemplate{
ID: workflowTemplateID,
},
Manifest: manifest,
IsLatest: latest,
Version: ts,
}
err = sb.Insert("workflow_template_versions").
SetMap(sq.Eq{
"workflow_template_id": workflowTemplateID,
"version": ts,
"is_latest": true,
"manifest": manifest,
}).
Suffix("RETURNING id").
RunWith(runner).
QueryRow().
Scan(&workflowTemplateVersion.ID)
return
}
// createLatestWorkflowTemplateVersionDB creates a new workflow template version and marks all previous versions as not latest.
func createLatestWorkflowTemplateVersionDB(runner sq.BaseRunner, workflowTemplateID uint64, manifest string) (workflowTemplateVersion *WorkflowTemplateVersion, err error) {
_, err = sb.Update("workflow_template_versions").
Set("is_latest", false).
Where(sq.Eq{
"workflow_template_id": workflowTemplateID,
}).
RunWith(runner).
Exec()
if err != nil {
return nil, err
}
return createWorkflowTemplateVersionDB(runner, workflowTemplateID, manifest, true)
}
// createWorkflowTemplate creates a WorkflowTemplate and all of the DB/Argo/K8s related resources
// The returned WorkflowTemplate has the ArgoWorkflowTemplate set to the newly created one.
func (c *Client) createWorkflowTemplate(namespace string, workflowTemplate *WorkflowTemplate) (*WorkflowTemplate, *WorkflowTemplateVersion, error) {
if err := workflowTemplate.GenerateUID(workflowTemplate.Name); err != nil {
return nil, nil, err
}
workflowTemplate.UID = uid
tx, err := c.DB.Begin()
if err != nil {
return nil, nil, err
}
defer tx.Rollback()
versionUnix := time.Now().Unix()
err = sb.Insert("workflow_templates").
SetMap(sq.Eq{
"uid": uid,
"uid": workflowTemplate.UID,
"name": workflowTemplate.Name,
"namespace": namespace,
"is_system": workflowTemplate.IsSystem,
@@ -50,33 +93,22 @@ func (c *Client) createWorkflowTemplate(namespace string, workflowTemplate *Work
return nil, nil, err
}
workflowTemplateVersion := &WorkflowTemplateVersion{}
err = sb.Insert("workflow_template_versions").
SetMap(sq.Eq{
"workflow_template_id": workflowTemplate.ID,
"version": versionUnix,
"is_latest": true,
"manifest": workflowTemplate.Manifest,
}).
Suffix("RETURNING id").
RunWith(tx).
QueryRow().
Scan(&workflowTemplateVersion.ID)
workflowTemplateVersion, err := createWorkflowTemplateVersionDB(tx, workflowTemplate.ID, workflowTemplate.Manifest, true)
if err != nil {
return nil, nil, err
}
workflowTemplate.WorkflowTemplateVersionID = workflowTemplateVersion.ID
_, err = c.InsertLabelsRunner(tx, TypeWorkflowTemplateVersion, workflowTemplateVersion.ID, workflowTemplate.Labels)
if err != nil {
return nil, nil, err
}
argoWft, err := createArgoWorkflowTemplate(workflowTemplate, versionUnix)
argoWft, err := createArgoWorkflowTemplate(workflowTemplate, workflowTemplateVersion.Version)
if err != nil {
return nil, nil, err
}
argoWft.Labels[label.WorkflowTemplateVersionUid] = strconv.FormatInt(versionUnix, 10)
argoWft.Labels[label.WorkflowTemplateVersionUid] = strconv.FormatInt(workflowTemplateVersion.Version, 10)
if workflowTemplate.Resource != nil && workflowTemplate.ResourceUID != nil {
if *workflowTemplate.Resource == TypeWorkspaceTemplate {
@@ -96,7 +128,8 @@ func (c *Client) createWorkflowTemplate(namespace string, workflowTemplate *Work
return nil, nil, err
}
workflowTemplate.Version = versionUnix
workflowTemplate.ArgoWorkflowTemplate = argoWft
workflowTemplate.Version = workflowTemplateVersion.Version
return workflowTemplate, workflowTemplateVersion, nil
}
@@ -131,6 +164,8 @@ func (c *Client) countWorkflowTemplateSelectBuilder(namespace string) sq.SelectB
return sb
}
// workflowTemplatesVersionSelectBuilder selects data from workflow template versions joined to a workflow template
// the versions/template are filtered by the workflow template's namespace.
func (c *Client) workflowTemplatesVersionSelectBuilder(namespace string) sq.SelectBuilder {
sb := sb.Select(getWorkflowTemplateVersionColumns("wtv")...).
From("workflow_template_versions wtv").
@@ -142,11 +177,14 @@ func (c *Client) workflowTemplatesVersionSelectBuilder(namespace string) sq.Sele
return sb
}
// GetWorkflowTemplateDB returns a WorkflowTemplate from the database
func (c *Client) GetWorkflowTemplateDB(namespace, name string) (workflowTemplate *WorkflowTemplate, err error) {
// GetWorkflowTemplateDB returns a WorkflowTemplate from the database that is not archived, should one exist.
func (c *Client) getWorkflowTemplateDB(namespace, name string) (workflowTemplate *WorkflowTemplate, err error) {
workflowTemplate = &WorkflowTemplate{}
sb := c.workflowTemplatesSelectBuilder(namespace).
Where(sq.Eq{
"name": name,
"wt.name": name,
"wt.is_archived": false,
})
err = c.DB.Getx(workflowTemplate, sb)
@@ -154,9 +192,11 @@ func (c *Client) GetWorkflowTemplateDB(namespace, name string) (workflowTemplate
return
}
// GetWorkflowTemplateVersionDB will return a WorkflowTemplateVersion given the arguments.
// getWorkflowTemplateVersionDB will return a WorkflowTemplateVersion given the arguments.
// version can be a number as a string, or the string "latest" to get the latest.
func (c *Client) GetWorkflowTemplateVersionDB(namespace, name, version string) (workflowTemplateVersion *WorkflowTemplateVersion, err error) {
func (c *Client) getWorkflowTemplateVersionDB(namespace, name, version string) (workflowTemplateVersion *WorkflowTemplateVersion, err error) {
workflowTemplateVersion = &WorkflowTemplateVersion{}
whereMap := sq.Eq{
"wt.name": name,
}
@@ -176,8 +216,8 @@ func (c *Client) GetWorkflowTemplateVersionDB(namespace, name, version string) (
}
// GetLatestWorkflowTemplateVersionDB returns the latest WorkflowTemplateVersion
func (c *Client) GetLatestWorkflowTemplateVersionDB(namespace, name string) (workflowTemplateVersion *WorkflowTemplateVersion, err error) {
return c.GetWorkflowTemplateVersionDB(namespace, name, "latest")
func (c *Client) getLatestWorkflowTemplateVersionDB(namespace, name string) (workflowTemplateVersion *WorkflowTemplateVersion, err error) {
return c.getWorkflowTemplateVersionDB(namespace, name, "latest")
}
func (c *Client) getWorkflowTemplateById(id uint64) (workflowTemplate *WorkflowTemplate, err error) {
@@ -197,13 +237,16 @@ func (c *Client) getWorkflowTemplateById(id uint64) (workflowTemplate *WorkflowT
return
}
// If version is 0, the latest workflow template is fetched.
// getWorkflowTemplate gets the workflowtemplate given the input data.
// it also loads the argo workflow and labels data.
// If version is <= 0, the latest workflow template is fetched.
// If not found, (nil, nil) is returned
func (c *Client) getWorkflowTemplate(namespace, uid string, version int64) (workflowTemplate *WorkflowTemplate, err error) {
workflowTemplate = &WorkflowTemplate{
WorkflowExecutionStatisticReport: &WorkflowExecutionStatisticReport{},
}
// A new workflow template version is created upon a change, so we use it's createdAt
// A new workflow template version is created upon a change, so we use it's created_at
// as a modified_at for the workflow template.
sb := c.workflowTemplatesSelectBuilder(namespace).
Columns("wtv.manifest", "wtv.version", "wtv.id workflow_template_version_id", "wtv.created_at modified_at").
@@ -213,7 +256,7 @@ func (c *Client) getWorkflowTemplate(namespace, uid string, version int64) (work
"wt.is_archived": false,
})
if version == 0 {
if version <= 0 {
sb = sb.Where(sq.Eq{"wtv.is_latest": true})
} else {
sb = sb.Where(sq.Eq{"wtv.version": version})
@@ -256,7 +299,7 @@ func (c *Client) getWorkflowTemplate(namespace, uid string, version int64) (work
}
func (c *Client) listWorkflowTemplateVersions(namespace, uid string) (workflowTemplateVersions []*WorkflowTemplate, err error) {
dbVersions, err := c.listDBWorkflowTemplateVersions(namespace, uid)
dbVersions, err := c.selectWorkflowTemplateVersionsDB(namespace, uid)
if err != nil {
return nil, err
}
@@ -291,8 +334,10 @@ func (c *Client) listWorkflowTemplateVersions(namespace, uid string) (workflowTe
return
}
func (c *Client) listWorkflowTemplates(namespace string, paginator *pagination.PaginationRequest) (workflowTemplateVersions []*WorkflowTemplate, err error) {
workflowTemplateVersions = []*WorkflowTemplate{}
// selectWorkflowTemplatesDB loads workflow templates from the database for the input namespace
// it also selects the total number of versions and latest version id
func (c *Client) selectWorkflowTemplatesDB(namespace string, paginator *pagination.PaginationRequest) (workflowTemplates []*WorkflowTemplate, err error) {
workflowTemplates = make([]*WorkflowTemplate, 0)
sb := c.workflowTemplatesSelectBuilder(namespace).
Column("COUNT(wtv.*) versions, MAX(wtv.id) workflow_template_version_id").
@@ -303,28 +348,24 @@ func (c *Client) listWorkflowTemplates(namespace string, paginator *pagination.P
"wt.is_system": false,
}).
OrderBy("wt.created_at DESC")
sb = *paginator.ApplyToSelect(&sb)
query, args, err := sb.ToSql()
if err != nil {
return
}
err = c.DB.Select(&workflowTemplateVersions, query, args...)
err = c.DB.Selectx(&workflowTemplates, sb)
return
}
// CountWorkflowTemplates counts the total number of workflow templates for the given namespace
// archived, and system templates are ignored.
func (c *Client) CountWorkflowTemplates(namespace string) (count int, err error) {
err = sb.Select("COUNT( DISTINCT( wt.id ))").
err = sb.Select("COUNT(*)").
From("workflow_templates wt").
Join("workflow_template_versions wtv ON wtv.workflow_template_id = wt.id").
Where(sq.Eq{
"wt.namespace": namespace,
"wt.is_archived": false,
"wt.is_system": false,
}).
RunWith(c.DB.DB).
RunWith(c.DB).
QueryRow().
Scan(&count)
@@ -368,6 +409,11 @@ func (c *Client) CreateWorkflowTemplate(namespace string, workflowTemplate *Work
return newWorkflowTemplate, nil
}
// CreateWorkflowTemplateVersion creates a new workflow template version including argo resources.
// It marks any older workflow template versions as not latest
//
// Pre-condition: a Workflow Template version already exists
// Post-condition: the input workflow template will have it's fields updated so it matches the new version data.
func (c *Client) CreateWorkflowTemplateVersion(namespace string, workflowTemplate *WorkflowTemplate) (*WorkflowTemplate, error) {
if workflowTemplate.UID == "" {
return nil, fmt.Errorf("uid required for CreateWorkflowTemplateVersion")
@@ -378,8 +424,6 @@ func (c *Client) CreateWorkflowTemplateVersion(namespace string, workflowTemplat
return nil, util.NewUserError(codes.InvalidArgument, err.Error())
}
versionUnix := time.Now().Unix()
tx, err := c.DB.Begin()
if err != nil {
return nil, err
@@ -391,75 +435,26 @@ func (c *Client) CreateWorkflowTemplateVersion(namespace string, workflowTemplat
"wt.uid": workflowTemplate.UID,
"wt.is_archived": false,
})
query, args, err := wftSb.ToSql()
if err != nil {
return nil, err
}
workflowTemplateDb := &WorkflowTemplate{}
if err = c.DB.Get(workflowTemplateDb, query, args...); err != nil {
workflowTemplateDB := &WorkflowTemplate{}
if err = c.DB.Getx(workflowTemplateDB, wftSb); err != nil {
return nil, err
}
_, err = sb.Update("workflow_template_versions").
Set("is_latest", false).
Where(sq.Eq{
"workflow_template_id": workflowTemplateDb.ID,
}).
RunWith(tx).
Exec()
workflowTemplateVersion, err := createLatestWorkflowTemplateVersionDB(tx, workflowTemplateDB.ID, workflowTemplate.Manifest)
if err != nil {
return nil, err
}
workflowTemplate.WorkflowTemplateVersionID = workflowTemplateVersion.ID
workflowTemplateVersionID := uint64(0)
err = sb.Insert("workflow_template_versions").
SetMap(sq.Eq{
"workflow_template_id": workflowTemplateDb.ID,
"version": versionUnix,
"is_latest": true,
"manifest": workflowTemplate.Manifest,
}).
Suffix("RETURNING id").
RunWith(tx).
QueryRow().
Scan(&workflowTemplateVersionID)
updatedTemplate, err := createArgoWorkflowTemplate(workflowTemplate, workflowTemplateVersion.Version)
if err != nil {
return nil, err
}
workflowTemplate.WorkflowTemplateVersionID = workflowTemplateVersionID
latest, err := c.getArgoWorkflowTemplate(namespace, workflowTemplate.UID, "latest")
if err != nil {
log.WithFields(log.Fields{
"Namespace": namespace,
"WorkflowTemplate": workflowTemplate,
"Error": err.Error(),
}).Error("Could not get latest argo workflow template")
return nil, err
}
delete(latest.Labels, label.VersionLatest)
latest, err = c.ArgoprojV1alpha1().WorkflowTemplates(namespace).Update(latest)
if err != nil {
return nil, err
}
updatedTemplate, err := createArgoWorkflowTemplate(workflowTemplate, versionUnix)
if err != nil {
latest.Labels[label.VersionLatest] = "true"
if _, err := c.ArgoprojV1alpha1().WorkflowTemplates(namespace).Update(latest); err != nil {
return nil, err
}
return nil, err
}
updatedTemplate.TypeMeta = v1.TypeMeta{}
updatedTemplate.ObjectMeta.ResourceVersion = ""
updatedTemplate.ObjectMeta.SetSelfLink("")
updatedTemplate.Labels[label.WorkflowTemplateVersionUid] = strconv.FormatInt(versionUnix, 10)
updatedTemplate.Labels[label.WorkflowTemplateVersionUid] = strconv.FormatInt(workflowTemplateVersion.Version, 10)
parametersMap, err := workflowTemplate.GetParametersKeyString()
if err != nil {
@@ -473,15 +468,32 @@ func (c *Client) CreateWorkflowTemplateVersion(namespace string, workflowTemplat
updatedTemplate.Annotations[key] = value
}
latest, err := c.getArgoWorkflowTemplate(namespace, workflowTemplate.UID, "latest")
if err != nil {
log.WithFields(log.Fields{
"Namespace": namespace,
"WorkflowTemplate": workflowTemplate,
"Error": err.Error(),
}).Error("Could not get latest argo workflow template")
return nil, err
}
delete(latest.Labels, label.VersionLatest)
if _, err := c.ArgoprojV1alpha1().WorkflowTemplates(namespace).Create(updatedTemplate); err != nil {
return nil, err
}
latest, err = c.ArgoprojV1alpha1().WorkflowTemplates(namespace).Update(latest)
if err != nil {
return nil, err
}
if err := tx.Commit(); err != nil {
return nil, err
}
workflowTemplate.Version = versionUnix
workflowTemplate.Version = workflowTemplateVersion.Version
return workflowTemplate, nil
}
@@ -564,7 +576,7 @@ func (c *Client) ListWorkflowTemplateVersions(namespace, uid string) (workflowTe
}
func (c *Client) ListWorkflowTemplates(namespace string, paginator *pagination.PaginationRequest) (workflowTemplateVersions []*WorkflowTemplate, err error) {
workflowTemplateVersions, err = c.listWorkflowTemplates(namespace, paginator)
workflowTemplateVersions, err = c.selectWorkflowTemplatesDB(namespace, paginator)
if err != nil {
log.WithFields(log.Fields{
"Namespace": namespace,
@@ -722,6 +734,8 @@ func (c *Client) ArchiveWorkflowTemplate(namespace, uid string) (archived bool,
return true, nil
}
// createArgoWorkflowTemplate creates an argo workflow template from the workflowTemplate struct
// the argo template stores the version information.
func createArgoWorkflowTemplate(workflowTemplate *WorkflowTemplate, version int64) (*v1alpha1.WorkflowTemplate, error) {
var argoWft *v1alpha1.WorkflowTemplate
var jsonOpts []argojson.JSONOpt
@@ -737,15 +751,14 @@ func createArgoWorkflowTemplate(workflowTemplate *WorkflowTemplate, version int6
return nil, err
}
worfklowTemplateName, err := uid2.GenerateUID(workflowTemplate.Name, 30)
if err != nil {
if err := workflowTemplate.GenerateUID(workflowTemplate.Name); err != nil {
return nil, err
}
argoWft.Name = fmt.Sprintf("%v-v%v", worfklowTemplateName, version)
argoWft.Name = fmt.Sprintf("%v-v%v", workflowTemplate.UID, version)
labels := map[string]string{
label.WorkflowTemplate: worfklowTemplateName,
label.WorkflowTemplate: workflowTemplate.UID,
label.WorkflowTemplateUid: workflowTemplate.UID,
label.Version: fmt.Sprintf("%v", version),
label.VersionLatest: "true",
@@ -757,9 +770,10 @@ func createArgoWorkflowTemplate(workflowTemplate *WorkflowTemplate, version int6
return argoWft, nil
}
// version "latest" will get the latest version.
func (c *Client) getArgoWorkflowTemplate(namespace, workflowTemplateUid, version string) (*v1alpha1.WorkflowTemplate, error) {
labelSelect := fmt.Sprintf("%v=%v", label.WorkflowTemplateUid, workflowTemplateUid)
// getArgoWorkflowTemplate will load the argo workflow template.
// version "latest" will get the latest version, otherwise a number (as a string) will be used.
func (c *Client) getArgoWorkflowTemplate(namespace, workflowTemplateUID, version string) (*v1alpha1.WorkflowTemplate, error) {
labelSelect := fmt.Sprintf("%v=%v", label.WorkflowTemplateUid, workflowTemplateUID)
if version == "latest" {
labelSelect += "," + label.VersionLatest + "=true"
} else {
@@ -799,28 +813,22 @@ func (c *Client) listArgoWorkflowTemplates(namespace, workflowTemplateUid string
return &templates, nil
}
func (c *Client) listDBWorkflowTemplateVersions(namespace, workflowTemplateUID string) ([]*WorkflowTemplateVersion, error) {
versions := make([]*WorkflowTemplateVersion, 0)
// listDBWorkflowTemplateVersions gets all of the workflow template versions for a specified workflow template uid
// archived ones are ignored. Returned in created_at desc order.
func (c *Client) selectWorkflowTemplateVersionsDB(namespace, workflowTemplateUID string) (versions []*WorkflowTemplateVersion, err error) {
versions = make([]*WorkflowTemplateVersion, 0)
sb := c.workflowTemplatesVersionSelectBuilder(namespace).
Columns(`wt.id "workflow_template.id"`, `wt.created_at "workflow_template.created_at"`).
Columns(`wt.name "workflow_template.name"`, `wt.is_archived "workflow_template.is_archived"`).
Columns(getWorkflowTemplateColumns("wt", "workflow_template")...).
Where(sq.Eq{
"wt.uid": workflowTemplateUID,
"wt.is_archived": false,
}).
OrderBy("wtv.created_at DESC")
query, args, err := sb.ToSql()
if err != nil {
return versions, err
}
err = c.DB.Selectx(&versions, sb)
if err := c.DB.Select(&versions, query, args...); err != nil {
return versions, err
}
return versions, nil
return
}
// prefix is the label prefix.

View File

@@ -0,0 +1,345 @@
package v1
import (
"database/sql"
"fmt"
"github.com/onepanelio/core/pkg/util"
"github.com/stretchr/testify/assert"
"google.golang.org/grpc/codes"
"testing"
)
const defaultWorkflowTemplate = `entrypoint: main
arguments:
parameters:
- name: source
value: https://github.com/onepanelio/pytorch-examples.git
- name: command
value: "python mnist/main.py --epochs=1"
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 2Gi
- metadata:
name: output
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 2Gi
templates:
- name: main
dag:
tasks:
- name: train-model
template: pytorch
# Uncomment section below to send metrics to Slack
# - name: notify-in-slack
# dependencies: [train-model]
# template: slack-notify-success
# arguments:
# parameters:
# - name: status
# value: "{{tasks.train-model.status}}"
# artifacts:
# - name: metrics
# from: "{{tasks.train-model.outputs.artifacts.sys-metrics}}"
- name: pytorch
inputs:
artifacts:
- name: src
path: /mnt/src
git:
repo: "{{workflow.parameters.source}}"
outputs:
artifacts:
- name: model
path: /mnt/output
optional: true
archive:
none: {}
container:
image: pytorch/pytorch:latest
command: [sh,-c]
args: ["{{workflow.parameters.command}}"]
workingDir: /mnt/src
volumeMounts:
- name: data
mountPath: /mnt/data
- name: output
mountPath: /mnt/output
- name: slack-notify-success
container:
image: technosophos/slack-notify
command: [sh,-c]
args: ['SLACK_USERNAME=Worker SLACK_TITLE="{{workflow.name}} {{inputs.parameters.status}}" SLACK_ICON=https://www.gravatar.com/avatar/5c4478592fe00878f62f0027be59c1bd SLACK_MESSAGE=$(cat /tmp/metrics.json)} ./slack-notify']
inputs:
parameters:
- name: status
artifacts:
- name: metrics
path: /tmp/metrics.json
optional: true
`
// testClientGetWorkflowTemplateDBEmpty attempts to get a WorkflowTemplate when there isn't one.
// this should fail.
func testClientGetWorkflowTemplateDBEmpty(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
_, err := c.getWorkflowTemplateDB("test", "test")
assert.Equal(t, sql.ErrNoRows, err)
}
// testClientGetWorkflowTemplateDBExists gets a WorkflowTemplate when there is one
// this should succeed
func testClientGetWorkflowTemplateDBExists(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
workflowTemplate := &WorkflowTemplate{
Name: "test",
Manifest: defaultWorkflowTemplate,
}
_, err := c.CreateWorkflowTemplate("onepanel", workflowTemplate)
assert.Nil(t, err)
_, err = c.getWorkflowTemplateDB("onepanel", "test")
assert.Nil(t, err)
}
// TestClient_getWorkflowTemplateDB tests getting a workflow template from the database
func TestClient_getWorkflowTemplateDB(t *testing.T) {
testClientGetWorkflowTemplateDBEmpty(t)
testClientGetWorkflowTemplateDBExists(t)
}
// testClientCreateWorkflowTemplateSuccess makes sure a correct workflow template is created correctly
func testClientCreateWorkflowTemplateSuccess(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
workflowTemplate := &WorkflowTemplate{
Name: "test",
Manifest: defaultWorkflowTemplate,
}
wft, err := c.CreateWorkflowTemplate("onepanel", workflowTemplate)
assert.Nil(t, err)
assert.NotNil(t, wft.ArgoWorkflowTemplate)
}
// testClientCreateWorkflowTemplateTimestamp makes sure we can create mulitple
// workflow templtate versions one after another with practically no time delay.
// This handles an edge case where versions were set using second time precision and could fail in migrations
// as they were created one after another.
func testClientCreateWorkflowTemplateTimestamp(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
namespace := "onepanel"
workflowTemplate := &WorkflowTemplate{
Name: "test",
Manifest: defaultWorkflowTemplate,
}
// This method creates a workflow template version underneath
wft, err := c.CreateWorkflowTemplate("onepanel", workflowTemplate)
assert.Nil(t, err)
assert.NotNil(t, wft.ArgoWorkflowTemplate)
// This method creates a brand new version
wft, err = c.CreateWorkflowTemplateVersion(namespace, workflowTemplate)
assert.Nil(t, err)
assert.NotNil(t, wft.ArgoWorkflowTemplate)
}
// testClientCreateWorkflowTemplateInsertSameName attempts to insert a WorkflowTemplate with the same name
// this should fail
func testClientCreateWorkflowTemplateInsertSameName(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
workflowTemplate := &WorkflowTemplate{
Name: "test",
Manifest: defaultWorkflowTemplate,
}
_, err := c.CreateWorkflowTemplate("onepanel", workflowTemplate)
assert.Nil(t, err)
_, err = c.CreateWorkflowTemplate("onepanel", workflowTemplate)
assert.NotNil(t, err)
assert.IsType(t, &util.UserError{}, err)
userErr := err.(*util.UserError)
assert.Equal(t, userErr.Code, codes.AlreadyExists)
}
// TestClient_CreateWorkflowTemplate tests creating a workflow template
func TestClient_CreateWorkflowTemplate(t *testing.T) {
testClientCreateWorkflowTemplateInsertSameName(t)
testClientCreateWorkflowTemplateSuccess(t)
testClientCreateWorkflowTemplateTimestamp(t)
}
// testClientPrivateGetWorkflowTemplateSuccess gets a workflow template with no error conditions encountered
func testClientPrivateGetWorkflowTemplateSuccess(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
namespace := "onepanel"
workflowTemplate := &WorkflowTemplate{
Name: "test",
Manifest: defaultWorkflowTemplate,
}
created, _ := c.CreateWorkflowTemplate(namespace, workflowTemplate)
wt, err := c.getWorkflowTemplate(namespace, created.UID, 0)
assert.NotNil(t, wt)
assert.Nil(t, err)
}
// testClientGetWorkflowTemplateSuccessVersion gets a workflow template for a specific version with no error conditions encountered
func testClientPrivateGetWorkflowTemplateSuccessVersion(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
namespace := "onepanel"
workflowTemplate := &WorkflowTemplate{
Name: "test",
Manifest: defaultWorkflowTemplate,
}
created, _ := c.CreateWorkflowTemplate(namespace, workflowTemplate)
c.CreateWorkflowTemplateVersion(namespace, workflowTemplate)
wt, err := c.getWorkflowTemplate(namespace, created.UID, created.Version)
assert.NotNil(t, wt)
assert.Nil(t, err)
assert.Equal(t, created.Version, wt.Version)
assert.Equal(t, created.Manifest, wt.Manifest)
}
// testClientGetWorkflowTemplateNotFound attempts to get a not-found workflow template
func testClientPrivateGetWorkflowTemplateNotFound(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
wt, err := c.getWorkflowTemplate("onepanel", "uid-not-found", 0)
assert.Nil(t, wt)
assert.Nil(t, err)
}
// Test_getWorkflowTemplate tests getting a workflow template
func Test_getWorkflowTemplate(t *testing.T) {
testClientPrivateGetWorkflowTemplateSuccess(t)
testClientPrivateGetWorkflowTemplateNotFound(t)
testClientPrivateGetWorkflowTemplateSuccessVersion(t)
}
func TestClient_getWorkflowTemplateVersionDB(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
namespace := "onepanel"
name := "test"
workflowTemplate := &WorkflowTemplate{
Name: name,
Manifest: defaultWorkflowTemplate,
}
original, _ := c.CreateWorkflowTemplate(namespace, workflowTemplate)
versionAsString := fmt.Sprintf("%v", original.Version)
originalRes, err := c.getWorkflowTemplateVersionDB(namespace, name, versionAsString)
assert.Nil(t, err)
assert.Equal(t, original.Version, originalRes.Version)
}
// testClientCreateWorkflowTemplateVersionNew makes sure you can successfully create a new workflow template version
func testClientCreateWorkflowTemplateVersionNew(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
namespace := "onepanel"
workflowTemplate := &WorkflowTemplate{
Name: "test",
Manifest: defaultWorkflowTemplate,
}
c.CreateWorkflowTemplate(namespace, workflowTemplate)
_, err := c.CreateWorkflowTemplateVersion(namespace, workflowTemplate)
assert.Nil(t, err)
}
// testClientCreateWorkflowTemplateVersionMarkOldNotLatest makes sure older versions are no longer marked as latest
func testClientCreateWorkflowTemplateVersionMarkOldNotLatest(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
namespace := "onepanel"
name := "test"
workflowTemplate := &WorkflowTemplate{
Name: name,
Manifest: defaultWorkflowTemplate,
}
original, _ := c.CreateWorkflowTemplate(namespace, workflowTemplate)
originalVersionAsString := fmt.Sprintf("%v", original.Version)
c.CreateWorkflowTemplateVersion(namespace, workflowTemplate)
updated, _ := c.getWorkflowTemplateVersionDB(namespace, name, originalVersionAsString)
assert.False(t, updated.IsLatest)
}
// Test_getWorkflowTemplate_SuccessVersion tests cases for creating a workflow template version
func TestClient_CreateWorkflowTemplateVersion(t *testing.T) {
testClientCreateWorkflowTemplateVersionNew(t)
testClientCreateWorkflowTemplateVersionMarkOldNotLatest(t)
}
// testGetWorkflowTemplateSuccess gets a workflow template with no error conditions encountered
func testClientGetWorkflowTemplateSuccess(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
namespace := "onepanel"
workflowTemplate := &WorkflowTemplate{
Name: "test",
Manifest: defaultWorkflowTemplate,
}
created, _ := c.CreateWorkflowTemplate(namespace, workflowTemplate)
wt, err := c.GetWorkflowTemplate(namespace, created.UID, 0)
assert.NotNil(t, wt)
assert.Nil(t, err)
}
// testGetWorkflowTemplateNotFound attempts to get a not-found workflow template
func testClientGetWorkflowTemplateNotFound(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
wt, err := c.GetWorkflowTemplate("onepanel", "uid-not-found", 0)
assert.Nil(t, wt)
userErr, ok := err.(*util.UserError)
assert.True(t, ok)
assert.Equal(t, codes.NotFound, userErr.Code)
}
func TestClient_GetWorkflowTemplate(t *testing.T) {
testClientGetWorkflowTemplateSuccess(t)
testClientGetWorkflowTemplateNotFound(t)
}

View File

@@ -4,6 +4,7 @@ import (
"encoding/json"
wfv1 "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1"
"github.com/onepanelio/core/pkg/util/mapping"
uid2 "github.com/onepanelio/core/pkg/util/uid"
"github.com/onepanelio/core/util/sql"
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v2"
@@ -13,6 +14,9 @@ import (
// WorkflowTemplate represents a Workflow Template backed by a database row
// it stores information required to run an execution
// A Workflow template is uniquely identified by
// (namespace, uid, is_archived)
// (namespace, name, is_archived) -- because we create a uid from the name.
type WorkflowTemplate struct {
ID uint64
CreatedAt time.Time `db:"created_at"`
@@ -35,6 +39,18 @@ type WorkflowTemplate struct {
ResourceUID *string // see Resource field
}
// GenerateUID generates a uid from the input name and sets it on the workflow template
func (wt *WorkflowTemplate) GenerateUID(name string) error {
result, err := uid2.GenerateUID(name, 30)
if err != nil {
return err
}
wt.UID = result
return nil
}
// GetManifestBytes returns the manifest as []byte
func (wt *WorkflowTemplate) GetManifestBytes() []byte {
return []byte(wt.Manifest)

View File

@@ -10,9 +10,9 @@ import (
"github.com/onepanelio/core/pkg/util"
"github.com/onepanelio/core/pkg/util/pagination"
"github.com/onepanelio/core/pkg/util/ptr"
uid2 "github.com/onepanelio/core/pkg/util/uid"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc/codes"
"strings"
"time"
)
@@ -33,16 +33,55 @@ func (c *Client) workspacesSelectBuilder(namespace string) sq.SelectBuilder {
return sb
}
func getWorkspaceParameterValue(parameters []Parameter, name string) *string {
for _, p := range parameters {
if p.Name == name {
return p.Value
}
// workspaceStatusToFieldMap takes a status and creates a map of the fields that should be updated
func workspaceStatusToFieldMap(status *WorkspaceStatus) sq.Eq {
fieldMap := sq.Eq{
"phase": status.Phase,
"modified_at": time.Now().UTC(),
}
switch status.Phase {
case WorkspaceLaunching:
fieldMap["paused_at"] = pq.NullTime{}
fieldMap["started_at"] = time.Now().UTC()
break
case WorkspacePausing:
fieldMap["started_at"] = pq.NullTime{}
fieldMap["paused_at"] = time.Now().UTC()
break
case WorkspaceUpdating:
fieldMap["paused_at"] = pq.NullTime{}
fieldMap["updated_at"] = time.Now().UTC()
break
case WorkspaceTerminating:
fieldMap["started_at"] = pq.NullTime{}
fieldMap["paused_at"] = pq.NullTime{}
fieldMap["terminated_at"] = time.Now().UTC()
break
}
return nil
return fieldMap
}
// updateWorkspaceStatusBuilder creates an update builder that updates a workspace's status and related fields to match that status.
func updateWorkspaceStatusBuilder(namespace, uid string, status *WorkspaceStatus) sq.UpdateBuilder {
fieldMap := workspaceStatusToFieldMap(status)
ub := sb.Update("workspaces").
SetMap(fieldMap).
Where(sq.And{
sq.Eq{
"namespace": namespace,
"uid": uid,
}, sq.NotEq{
"phase": WorkspaceTerminated,
},
})
return ub
}
// mergeWorkspaceParameters combines two parameter arrays. If a parameter in newParameters is not in
// the existing ones, it is added. If it is, it is ignored.
func mergeWorkspaceParameters(existingParameters, newParameters []Parameter) (parameters []Parameter) {
parameterMap := make(map[string]*string, 0)
for _, p := range newParameters {
@@ -74,10 +113,6 @@ func mergeWorkspaceParameters(existingParameters, newParameters []Parameter) (pa
// sys-resource-action
// sys-host
func injectWorkspaceSystemParameters(namespace string, workspace *Workspace, workspaceAction, resourceAction string, config SystemConfig) (err error) {
workspace.UID, err = uid2.GenerateUID(workspace.Name, 30)
if err != nil {
return
}
host := fmt.Sprintf("%v--%v.%v", workspace.UID, namespace, *config.Domain())
systemParameters := []Parameter{
{
@@ -98,6 +133,10 @@ func injectWorkspaceSystemParameters(namespace string, workspace *Workspace, wor
return
}
// createWorkspace creates a workspace and related resources.
// The following are required on the workspace:
// WorkspaceTemplate.WorkflowTemplate.UID
// WorkspaceTemplate.WorkflowTemplate.Version
func (c *Client) createWorkspace(namespace string, parameters []byte, workspace *Workspace) (*Workspace, error) {
systemConfig, err := c.GetSystemConfig()
if err != nil {
@@ -155,7 +194,11 @@ func (c *Client) createWorkspace(namespace string, parameters []byte, workspace
QueryRow().
Scan(&workspace.ID, &workspace.CreatedAt)
if err != nil {
return nil, util.NewUserErrorWrap(err, "Workspace")
if strings.Contains(err.Error(), "invalid input syntax for type json") {
return nil, util.NewUserError(codes.InvalidArgument, err.Error())
}
return nil, util.NewUserError(codes.Unknown, err.Error())
}
return workspace, nil
@@ -168,6 +211,10 @@ func (c *Client) CreateWorkspace(namespace string, workspace *Workspace) (*Works
return nil, err
}
if err := workspace.GenerateUID(workspace.Name); err != nil {
return nil, err
}
parameters, err := json.Marshal(workspace.Parameters)
if err != nil {
return nil, err
@@ -182,7 +229,7 @@ func (c *Client) CreateWorkspace(namespace string, workspace *Workspace) (*Works
Value: ptr.String(workspace.UID),
})
sysHost := getWorkspaceParameterValue(workspace.Parameters, "sys-host")
sysHost := workspace.GetParameterValue("sys-host")
if sysHost == nil {
return nil, fmt.Errorf("sys-host parameter not found")
}
@@ -267,51 +314,31 @@ func (c *Client) GetWorkspace(namespace, uid string) (workspace *Workspace, err
// UpdateWorkspaceStatus updates workspace status and times based on phase
func (c *Client) UpdateWorkspaceStatus(namespace, uid string, status *WorkspaceStatus) (err error) {
fieldMap := sq.Eq{
"phase": status.Phase,
"modified_at": time.Now().UTC(),
}
switch status.Phase {
case WorkspaceLaunching:
fieldMap["paused_at"] = pq.NullTime{}
fieldMap["started_at"] = time.Now().UTC()
break
case WorkspacePausing:
fieldMap["started_at"] = pq.NullTime{}
fieldMap["paused_at"] = time.Now().UTC()
break
case WorkspaceUpdating:
fieldMap["paused_at"] = pq.NullTime{}
fieldMap["updated_at"] = time.Now().UTC()
break
case WorkspaceTerminating:
fieldMap["started_at"] = pq.NullTime{}
fieldMap["paused_at"] = pq.NullTime{}
fieldMap["terminated_at"] = time.Now().UTC()
break
}
_, err = sb.Update("workspaces").
SetMap(fieldMap).
Where(sq.And{
sq.Eq{
"namespace": namespace,
"uid": uid,
}, sq.NotEq{
"phase": WorkspaceTerminated,
},
}).
RunWith(c.DB).Exec()
result, err := updateWorkspaceStatusBuilder(namespace, uid, status).
RunWith(c.DB).
Exec()
if err != nil {
return err
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
return util.NewUserError(codes.NotFound, "Workspace not found.")
}
return
}
// ListWorkspacesByTemplateID will return all the workspaces for a given workspace template id.
// ListWorkspacesByTemplateID will return all the workspaces for a given workspace template id that are not terminated.
// Sourced from database.
// Includes labels.
func (c *Client) ListWorkspacesByTemplateID(namespace string, templateID uint64) (workspaces []*Workspace, err error) {
sb := sb.Select(getWorkspaceColumns("w")...).
Columns(getWorkspaceStatusColumns("w", "status")...).
From("workspaces w").
Where(sq.And{
sq.Eq{
@@ -391,6 +418,7 @@ func (c *Client) CountWorkspaces(namespace string) (count int, err error) {
return
}
// updateWorkspace updates the workspace to the indicated status
func (c *Client) updateWorkspace(namespace, uid, workspaceAction, resourceAction string, status *WorkspaceStatus, parameters ...Parameter) (err error) {
workspace, err := c.GetWorkspace(namespace, uid)
if err != nil {
@@ -444,32 +472,15 @@ func (c *Client) updateWorkspace(namespace, uid, workspaceAction, resourceAction
return
}
if err = c.UpdateWorkspaceStatus(namespace, uid, status); err != nil {
return
sb := updateWorkspaceStatusBuilder(namespace, uid, status)
// Update parameters if they are passed in
if len(parameters) != 0 {
sb.Set("parameters", parametersJSON)
}
// Update parameters if they are passed
if len(parameters) == 0 {
return
}
_, err = sb.Update("workspaces").
SetMap(sq.Eq{
"parameters": parametersJSON,
}).
Where(sq.And{
sq.Eq{
"namespace": namespace,
"uid": uid,
}, sq.NotEq{
"phase": WorkspaceTerminated,
},
}).
RunWith(c.DB).
_, err = sb.RunWith(c.DB).
Exec()
if err != nil {
return util.NewUserError(codes.NotFound, "Workspace not found.")
}
return
}
@@ -492,6 +503,6 @@ func (c *Client) DeleteWorkspace(namespace, uid string) (err error) {
// ArchiveWorkspace archives by setting the workspace to delete or terminate.
// Kicks off DB archiving and k8s cleaning.
func (c *Client) ArchiveWorkspace(namespace, uid string) (err error) {
return c.updateWorkspace(namespace, uid, "delete", "delete", &WorkspaceStatus{Phase: WorkspaceTerminating})
func (c *Client) ArchiveWorkspace(namespace, uid string, parameters ...Parameter) (err error) {
return c.updateWorkspace(namespace, uid, "delete", "delete", &WorkspaceStatus{Phase: WorkspaceTerminating}, parameters...)
}

View File

@@ -12,7 +12,6 @@ import (
"github.com/onepanelio/core/pkg/util/env"
"github.com/onepanelio/core/pkg/util/pagination"
"github.com/onepanelio/core/pkg/util/ptr"
uid2 "github.com/onepanelio/core/pkg/util/uid"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc/codes"
networking "istio.io/api/networking/v1alpha3"
@@ -84,13 +83,20 @@ func generateRuntimeParameters(config SystemConfig) (parameters []Parameter, err
})
// Node pool parameter and options
options, err := config.NodePoolOptions()
nodePoolOptions, err := config.NodePoolOptions()
if err != nil {
return nil, err
}
if len(options) == 0 {
if len(nodePoolOptions) == 0 {
return nil, fmt.Errorf("no node pool options in config")
}
var options []*ParameterOption
for _, option := range nodePoolOptions {
options = append(options, &ParameterOption{
Name: option.Name,
Value: option.Value,
})
}
parameters = append(parameters, Parameter{
Name: "sys-node-pool-label",
@@ -111,82 +117,6 @@ func generateRuntimeParameters(config SystemConfig) (parameters []Parameter, err
return
}
func generateStaticParameters() (parameters []Parameter, err error) {
parameters = make([]Parameter, 0)
// Resource action parameter
parameters = append(parameters, Parameter{
Name: "sys-name",
Type: "input.text",
Value: ptr.String("name"),
DisplayName: ptr.String("Workspace name"),
Hint: ptr.String("Must be between 3-30 characters, contain only alphanumeric or `-` characters"),
Required: true,
})
// TODO: These can be removed when lint validation of workflows work
// Resource action parameter
parameters = append(parameters, Parameter{
Name: "sys-resource-action",
Value: ptr.String("apply"),
Type: "input.hidden",
})
// Workspace action
parameters = append(parameters, Parameter{
Name: "sys-workspace-action",
Value: ptr.String("create"),
Type: "input.hidden",
})
// UID placeholder
parameters = append(parameters, Parameter{
Name: "sys-uid",
Value: ptr.String("uid"),
Type: "input.hidden",
})
return
}
func generateVolumeParameters(spec *WorkspaceSpec) (parameters []Parameter, err error) {
if spec == nil {
return nil, fmt.Errorf("workspaceSpec is nil")
}
parameters = make([]Parameter, 0)
// Map all the volumeClaimTemplates that have storage set
volumeStorageQuantityIsSet := make(map[string]bool)
for _, v := range spec.VolumeClaimTemplates {
if v.Spec.Resources.Requests != nil {
volumeStorageQuantityIsSet[v.ObjectMeta.Name] = true
}
}
// Volume size parameters
volumeClaimsMapped := make(map[string]bool)
for _, c := range spec.Containers {
for _, v := range c.VolumeMounts {
// Skip if already mapped or storage size is set
if volumeClaimsMapped[v.Name] || volumeStorageQuantityIsSet[v.Name] {
continue
}
parameters = append(parameters, Parameter{
Name: fmt.Sprintf("sys-%v-volume-size", v.Name),
Type: "input.number",
Value: ptr.String("20480"),
DisplayName: ptr.String(fmt.Sprintf("Disk size for \"%v\"", v.Name)),
Hint: ptr.String(fmt.Sprintf("Disk size in MB for volume mounted at `%v`", v.MountPath)),
Required: true,
})
volumeClaimsMapped[v.Name] = true
}
}
return
}
func generateArguments(spec *WorkspaceSpec, config SystemConfig) (err error) {
systemParameters := make([]Parameter, 0)
// Resource action parameter
@@ -360,7 +290,7 @@ func createStatefulSetManifest(spec *WorkspaceSpec, config map[string]string) (s
env.PrependEnvVarToContainer(container, "ONEPANEL_API_URL", config["ONEPANEL_API_URL"])
env.PrependEnvVarToContainer(container, "ONEPANEL_FQDN", config["ONEPANEL_FQDN"])
env.PrependEnvVarToContainer(container, "ONEPANEL_DOMAIN", config["ONEPANEL_DOMAIN"])
env.PrependEnvVarToContainer(container, "ONEPANEL_PROVIDER_TYPE", config["PROVIDER_TYPE"])
env.PrependEnvVarToContainer(container, "ONEPANEL_PROVIDER", config["ONEPANEL_PROVIDER"])
env.PrependEnvVarToContainer(container, "ONEPANEL_RESOURCE_NAMESPACE", "{{workflow.namespace}}")
env.PrependEnvVarToContainer(container, "ONEPANEL_RESOURCE_UID", "{{workflow.parameters.sys-name}}")
@@ -719,15 +649,14 @@ metadata:
}
func (c *Client) createWorkspaceTemplate(namespace string, workspaceTemplate *WorkspaceTemplate) (*WorkspaceTemplate, error) {
uid, err := uid2.GenerateUID(workspaceTemplate.Name, 30)
err := workspaceTemplate.GenerateUID(workspaceTemplate.Name)
if err != nil {
return nil, err
}
workspaceTemplate.UID = uid
workspaceTemplate.WorkflowTemplate.IsSystem = true
workspaceTemplate.WorkflowTemplate.Resource = ptr.String(TypeWorkspaceTemplate)
workspaceTemplate.WorkflowTemplate.ResourceUID = ptr.String(uid)
workspaceTemplate.WorkflowTemplate.ResourceUID = &workspaceTemplate.UID
// validate workflow template
if err := c.validateWorkflowTemplate(namespace, workspaceTemplate.WorkflowTemplate); err != nil {
@@ -754,7 +683,7 @@ func (c *Client) createWorkspaceTemplate(namespace string, workspaceTemplate *Wo
defer tx.Rollback()
err = sb.Insert("workspace_templates").
SetMap(sq.Eq{
"uid": uid,
"uid": workspaceTemplate.UID,
"name": workspaceTemplate.Name,
"namespace": namespace,
"workflow_template_id": workspaceTemplate.WorkflowTemplate.ID,
@@ -1042,26 +971,23 @@ func (c *Client) UpdateWorkspaceTemplate(namespace string, workspaceTemplate *Wo
return workspaceTemplate, nil
}
// ListWorkspaceTemplates returns a list of workspace templates that are not archived, sorted by most recent created first
func (c *Client) ListWorkspaceTemplates(namespace string, paginator *pagination.PaginationRequest) (workspaceTemplates []*WorkspaceTemplate, err error) {
sb := c.workspaceTemplatesSelectBuilder(namespace).
Where(sq.Eq{
"wt.is_archived": false,
}).
OrderBy("wt.created_at DESC")
sb = *paginator.ApplyToSelect(&sb)
query, args, err := sb.ToSql()
if err != nil {
return nil, err
}
if err := c.DB.Select(&workspaceTemplates, query, args...); err != nil {
return nil, err
}
err = c.DB.Selectx(&workspaceTemplates, sb)
return
}
// ListWorkspaceTemplateVersions returns an array of WorkspaceTemplates with the version information loaded. Latest id is first.
// Labels are also loaded.
func (c *Client) ListWorkspaceTemplateVersions(namespace, uid string) (workspaceTemplates []*WorkspaceTemplate, err error) {
sb := c.workspaceTemplateVersionsSelectBuilder(namespace, uid).
Options("DISTINCT ON (wtv.version) wtv.version,").
@@ -1070,11 +996,8 @@ func (c *Client) ListWorkspaceTemplateVersions(namespace, uid string) (workspace
"wft.is_archived": false,
}).
OrderBy("wtv.version DESC")
query, args, err := sb.ToSql()
if err != nil {
return
}
if err = c.DB.Select(&workspaceTemplates, query, args...); err != nil {
if err = c.DB.Selectx(&workspaceTemplates, sb); err != nil {
return
}
@@ -1092,6 +1015,7 @@ func (c *Client) ListWorkspaceTemplateVersions(namespace, uid string) (workspace
return
}
// CountWorkspaceTemplates returns the total number of non-archived workspace templates for the input namespace
func (c *Client) CountWorkspaceTemplates(namespace string) (count int, err error) {
err = sb.Select("count(*)").
From("workspace_templates wt").
@@ -1182,6 +1106,9 @@ func (c *Client) ArchiveWorkspaceTemplate(namespace string, uid string) (archive
}).Error("Get Workspace Template failed.")
return false, util.NewUserError(codes.Unknown, "Unable to archive workspace template.")
}
if wsTemp == nil {
return false, fmt.Errorf("not found")
}
wsList, err := c.ListWorkspacesByTemplateID(namespace, wsTemp.WorkspaceTemplateVersionID)
if err != nil {

View File

@@ -52,9 +52,78 @@ routes:
workspaceTemplate = WorkspaceTemplate{
Manifest: workspaceSpecManifest,
}
jupyterLabWorkspaceManifest = `# Docker containers that are part of the Workspace
containers:
- name: jupyterlab-tensorflow
image: jupyter/tensorflow-notebook
command: [start.sh, jupyter]
workingDir: /data
env:
- name: tornado
value: "{ 'headers': { 'Content-Security-Policy': \"frame-ancestors * 'self'\" } }"
- name: GRANT_SUDO
value: 1
- name: CHOWN_EXTRA
value: '/data'
- name: CHOWN_EXTRA_OPTS
value: '-R'
securityContext:
runAsUser: 0
allowPrivilegeEscalation: false
args:
- lab
- --LabApp.token=''
- --LabApp.allow_remote_access=True
- --LabApp.allow_origin="*"
- --LabApp.disable_check_xsrf=True
- --LabApp.trust_xheaders=True
- --LabApp.tornado_settings=$(tornado)
- --NotebookApp.notebook_dir='/data'
ports:
- containerPort: 8888
name: jupyterlab
# Volumes to be mounted in this container
# Onepanel will automatically create these volumes and mount them to the container
volumeMounts:
- name: data
mountPath: /data
# Ports that need to be exposed
ports:
- name: jupyterlab
port: 80
protocol: TCP
targetPort: 8888
# Routes that will map to ports
routes:
- match:
- uri:
prefix: /
route:
- destination:
port:
number: 80
# DAG Workflow to be executed once a Workspace action completes
# postExecutionWorkflow:
# entrypoint: main
# templates:
# - name: main
# dag:
# tasks:
# - name: slack-notify
# template: slack-notify
# - name: slack-notify
# container:
# image: technosophos/slack-notify
# args:
# - SLACK_USERNAME=onepanel SLACK_TITLE="Your workspace is ready" SLACK_ICON=https://www.gravatar.com/avatar/5c4478592fe00878f62f0027be59c1bd SLACK_MESSAGE="Your workspace is now running" ./slack-notify
# command:
# - sh
# - -c
`
)
func TestParseWorkspaceSpec(t *testing.T) {
func Test_ParseWorkspaceSpec(t *testing.T) {
workspaceSpec, err := parseWorkspaceSpec(workspaceSpecManifest)
assert.Nil(t, err)
assert.NotEmpty(t, workspaceSpec)
@@ -65,3 +134,151 @@ func TestParseWorkspaceSpec(t *testing.T) {
assert.Equal(t, workspaceSpec.Containers[0].Ports[0].ContainerPort, int32(80))
assert.Equal(t, workspaceSpec.Containers[1].Ports[0].ContainerPort, int32(443))
}
// testClientCreateWorkspaceTemplateNew creates a new workspace template
func testClientCreateWorkspaceTemplateNew(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
namespace := "onepanel"
wt := &WorkspaceTemplate{
Name: "test",
Manifest: jupyterLabWorkspaceManifest,
}
_, err := c.CreateWorkspaceTemplate(namespace, wt)
assert.Nil(t, err)
}
// testClientCreateWorkspaceTemplateDuplicateName attempts to create a workspace template for a name that already exists
// this should error
func testClientCreateWorkspaceTemplateDuplicateName(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
namespace := "onepanel"
wt := &WorkspaceTemplate{
Name: "test",
Manifest: jupyterLabWorkspaceManifest,
}
_, err := c.CreateWorkspaceTemplate(namespace, wt)
_, err = c.CreateWorkspaceTemplate(namespace, wt)
assert.NotNil(t, err)
}
// testClientCreateWorkspaceTemplateArchivedName attempts to create a workspace template for a name that has been archived
// this should work
func testClientCreateWorkspaceTemplateArchivedName(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
namespace := "onepanel"
wt := &WorkspaceTemplate{
Name: "test",
Manifest: jupyterLabWorkspaceManifest,
}
wtCreated, err := c.CreateWorkspaceTemplate(namespace, wt)
_, err = c.ArchiveWorkspaceTemplate(namespace, wtCreated.UID)
_, err = c.CreateWorkspaceTemplate(namespace, wt)
assert.Nil(t, err)
}
// TestClient_CreateWorkspaceTemplate tests creating a workspace template
func TestClient_CreateWorkspaceTemplate(t *testing.T) {
testClientCreateWorkspaceTemplateNew(t)
testClientCreateWorkspaceTemplateDuplicateName(t)
testClientCreateWorkspaceTemplateArchivedName(t)
}
func testClientArchiveWorkspaceTemplateSuccess(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
namespace := "onepanel"
wt := &WorkspaceTemplate{
Name: "test",
Manifest: jupyterLabWorkspaceManifest,
}
testTemplate, _ := c.CreateWorkspaceTemplate(namespace, wt)
archived, err := c.ArchiveWorkspaceTemplate(namespace, testTemplate.UID)
assert.Nil(t, err)
assert.True(t, archived)
}
// testClientArchiveWorkspaceTemplateNotFound tests the case where you try to archive a non-existing workspace template
func testClientArchiveWorkspaceTemplateNotFound(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
namespace := "onepanel"
archived, err := c.ArchiveWorkspaceTemplate(namespace, "not-found")
assert.NotNil(t, err)
assert.False(t, archived)
}
// TestClient_ArchiveWorkspaceTemplate tests archiving a workspace template
func TestClient_ArchiveWorkspaceTemplate(t *testing.T) {
testClientArchiveWorkspaceTemplateSuccess(t)
testClientArchiveWorkspaceTemplateNotFound(t)
// TODO we need more tests here to make sure the related resources are cleaned up, including workspaces and workflow templates
}
// testClientListWorkspaceTemplatesEmpty tests listing workspace templates when there are none
func testClientListWorkspaceTemplatesEmpty(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
templates, err := c.ListWorkspaceTemplates("onepanel", nil)
assert.Nil(t, err)
assert.Empty(t, templates)
}
// testClientListWorkspaceTemplatesNotEmpty tests listing workspaces when there are records that are
// archived and not. It should only list the non-archived ones
func testClientListWorkspaceTemplatesNotEmpty(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
namespace := "onepanel"
wt := &WorkspaceTemplate{
Name: "test",
Manifest: jupyterLabWorkspaceManifest,
}
testTemplate, _ := c.CreateWorkspaceTemplate(namespace, wt)
c.ArchiveWorkspaceTemplate(namespace, testTemplate.UID)
wt2 := &WorkspaceTemplate{
Name: "test2",
Manifest: jupyterLabWorkspaceManifest,
}
wt2, _ = c.CreateWorkspaceTemplate(namespace, wt2)
templates, err := c.ListWorkspaceTemplates(namespace, nil)
assert.Nil(t, err)
assert.Equal(t, 1, len(templates))
assert.Equal(t, wt2.UID, templates[0].UID)
}
// TestClient_ListWorkspaceTemplates tests listing workspace templates
func TestClient_ListWorkspaceTemplates(t *testing.T) {
testClientListWorkspaceTemplatesEmpty(t)
testClientListWorkspaceTemplatesNotEmpty(t)
}

View File

@@ -3,6 +3,7 @@ package v1
import (
"fmt"
wfv1 "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1"
uid2 "github.com/onepanelio/core/pkg/util/uid"
"github.com/onepanelio/core/util/sql"
"sigs.k8s.io/yaml"
"time"
@@ -25,6 +26,18 @@ type WorkspaceTemplate struct {
WorkflowTemplateID uint64 `db:"workflow_template_id"`
}
// GenerateUID generates a uid from the input name and sets it on the workflow template
func (wt *WorkspaceTemplate) GenerateUID(name string) error {
result, err := uid2.GenerateUID(name, 30)
if err != nil {
return err
}
wt.UID = result
return nil
}
// InjectRuntimeParameters will inject all runtime variables into the WorkflowTemplate's manifest.
func (wt *WorkspaceTemplate) InjectRuntimeParameters(config SystemConfig) error {
if wt.WorkflowTemplate == nil {

314
pkg/workspace_test.go Normal file
View File

@@ -0,0 +1,314 @@
package v1
import (
"github.com/lib/pq"
"github.com/onepanelio/core/pkg/util"
"github.com/onepanelio/core/pkg/util/ptr"
"github.com/stretchr/testify/assert"
"google.golang.org/grpc/codes"
"testing"
"time"
)
func testWorkspaceStatusToFieldMapLaunching(t *testing.T) {
fm := workspaceStatusToFieldMap(&WorkspaceStatus{Phase: WorkspaceLaunching})
assert.Equal(t, fm["phase"], WorkspaceLaunching)
assert.Equal(t, fm["paused_at"], pq.NullTime{})
started := fm["started_at"].(time.Time)
assert.True(t, started.After(time.Time{}))
}
func testWorkspaceStatusToFieldMapPausing(t *testing.T) {
fm := workspaceStatusToFieldMap(&WorkspaceStatus{Phase: WorkspacePausing})
assert.Equal(t, fm["phase"], WorkspacePausing)
assert.Equal(t, fm["started_at"], pq.NullTime{})
paused := fm["paused_at"].(time.Time)
assert.True(t, paused.After(time.Time{}))
}
func testWorkspaceStatusToFieldMapUpdating(t *testing.T) {
fm := workspaceStatusToFieldMap(&WorkspaceStatus{Phase: WorkspaceUpdating})
assert.Equal(t, fm["phase"], WorkspaceUpdating)
assert.Equal(t, fm["paused_at"], pq.NullTime{})
updated := fm["updated_at"].(time.Time)
assert.True(t, updated.After(time.Time{}))
}
func testWorkspaceStatusToFieldMapTerminating(t *testing.T) {
fm := workspaceStatusToFieldMap(&WorkspaceStatus{Phase: WorkspaceTerminating})
assert.Equal(t, fm["phase"], WorkspaceTerminating)
assert.Equal(t, fm["paused_at"], pq.NullTime{})
assert.Equal(t, fm["started_at"], pq.NullTime{})
terminated := fm["terminated_at"].(time.Time)
assert.True(t, terminated.After(time.Time{}))
}
func Test_WorkspaceStatusToFieldMap(t *testing.T) {
testWorkspaceStatusToFieldMapLaunching(t)
testWorkspaceStatusToFieldMapPausing(t)
testWorkspaceStatusToFieldMapUpdating(t)
testWorkspaceStatusToFieldMapTerminating(t)
}
// testClientPrivateCreateWorkspaceNoWorkflowTemplate makes sure we get an error when there is no workflow template for the workspace
func testClientPrivateCreateWorkspaceNoWorkflowTemplate(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
namespace := "onepanel"
workspace := &Workspace{
Name: "test",
WorkspaceTemplate: &WorkspaceTemplate{
WorkflowTemplate: &WorkflowTemplate{
UID: "not-exist",
Version: 1,
},
},
}
workspace.GenerateUID("test")
_, err := c.createWorkspace(namespace, []byte(""), workspace)
userErr, ok := err.(*util.UserError)
assert.True(t, ok)
assert.Equal(t, userErr.Code, codes.NotFound)
}
// testClientPrivateCreateWorkspaceSuccess tests creating a workspace successfully
func testClientPrivateCreateWorkspaceSuccess(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
namespace := "onepanel"
workspaceTemplate := &WorkspaceTemplate{
Name: "test",
Manifest: jupyterLabWorkspaceManifest,
}
workspaceTemplate, _ = c.CreateWorkspaceTemplate(namespace, workspaceTemplate)
workspace := &Workspace{
Name: "test2",
WorkspaceTemplate: workspaceTemplate,
Parameters: []Parameter{
{
Name: "workflow-execution-name",
Value: ptr.String("test2"),
},
},
}
workspace.GenerateUID("test")
_, err := c.createWorkspace(namespace, []byte("{}"), workspace)
assert.Nil(t, err)
}
func TestClient_createWorkspace(t *testing.T) {
testClientPrivateCreateWorkspaceNoWorkflowTemplate(t)
testClientPrivateCreateWorkspaceSuccess(t)
}
func testClientCreateWorkspaceSuccess(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
namespace := "onepanel"
wt := &WorkspaceTemplate{
Name: "test",
Manifest: jupyterLabWorkspaceManifest,
}
testTemplate, _ := c.CreateWorkspaceTemplate(namespace, wt)
workspace := &Workspace{
Name: "test",
WorkspaceTemplate: &WorkspaceTemplate{
UID: testTemplate.UID,
Version: testTemplate.Version,
},
Parameters: []Parameter{
{
Name: "workflow-execution-name",
Value: ptr.String("test2"),
},
},
}
createdWorkspace, err := c.CreateWorkspace(namespace, workspace)
assert.Nil(t, err)
assert.NotNil(t, createdWorkspace)
}
func TestClient_CreateWorkspace(t *testing.T) {
testClientCreateWorkspaceSuccess(t)
}
func TestClient_ArchiveWorkspace(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
namespace := "onepanel"
workspaceTemplate := &WorkspaceTemplate{
Name: "test",
Manifest: jupyterLabWorkspaceManifest,
}
workspaceTemplate, _ = c.CreateWorkspaceTemplate(namespace, workspaceTemplate)
workspace := &Workspace{
Name: "test2",
WorkspaceTemplate: workspaceTemplate,
Parameters: []Parameter{
{
Name: "workflow-execution-name",
Value: ptr.String("test2"),
},
},
}
workspace.GenerateUID("test")
createdWorkspace, _ := c.createWorkspace(namespace, []byte("[]"), workspace)
params := []Parameter{
{
Name: "workflow-execution-name",
Value: ptr.String("test3"),
},
}
err := c.ArchiveWorkspace(namespace, createdWorkspace.UID, params...)
assert.Nil(t, err)
}
// TestClient_ListWorkspacesByTemplateID tests listing workspaces by the template id
func TestClient_ListWorkspacesByTemplateID(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
namespace := "onepanel"
wt := &WorkspaceTemplate{
Name: "test",
Manifest: jupyterLabWorkspaceManifest,
}
testTemplate, _ := c.CreateWorkspaceTemplate(namespace, wt)
workspace := &Workspace{
Name: "test",
WorkspaceTemplate: testTemplate,
Parameters: []Parameter{
{
Name: "workflow-execution-name",
Value: ptr.String("test"),
},
},
}
workspace.GenerateUID("test")
c.createWorkspace(namespace, []byte("[]"), workspace)
wt2 := &WorkspaceTemplate{
Name: "test2",
Manifest: jupyterLabWorkspaceManifest,
}
testTemplate2, _ := c.CreateWorkspaceTemplate(namespace, wt2)
workspace2 := &Workspace{
Name: "test2",
WorkspaceTemplate: testTemplate2,
Parameters: []Parameter{
{
Name: "workflow-execution-name",
Value: ptr.String("test2"),
},
},
}
workspace2.GenerateUID("test2")
c.createWorkspace(namespace, []byte("[]"), workspace2)
workspaces, err := c.ListWorkspacesByTemplateID(namespace, testTemplate.ID)
assert.Nil(t, err)
assert.Equal(t, 1, len(workspaces))
params := []Parameter{
{
Name: "workflow-execution-name",
Value: ptr.String("test3"),
},
}
c.ArchiveWorkspace(namespace, testTemplate.UID, params...)
workspaces, err = c.ListWorkspacesByTemplateID(namespace, testTemplate.ID)
assert.Nil(t, err)
assert.True(t, workspaces[0].Status.Phase == WorkspaceTerminating)
}
func testUpdateWorkspaceStatusSuccess(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
namespace := "onepanel"
workspace := &Workspace{
Name: "test",
Parameters: []Parameter{
{
Name: "workflow-execution-name",
Value: ptr.String("test"),
},
},
WorkspaceTemplate: &WorkspaceTemplate{
Name: "test",
Manifest: jupyterLabWorkspaceManifest,
WorkflowTemplate: &WorkflowTemplate{
UID: "test",
Version: 1,
},
},
}
workspace.GenerateUID("test")
c.CreateWorkspaceTemplate(namespace, workspace.WorkspaceTemplate)
ws, _ := c.createWorkspace(namespace, []byte("[]"), workspace)
err := c.UpdateWorkspaceStatus(namespace, ws.UID, &WorkspaceStatus{Phase: WorkspacePausing})
assert.Nil(t, err)
}
func testUpdateWorkspaceStatusNotFound(t *testing.T) {
c := DefaultTestClient()
clearDatabase(t)
err := c.UpdateWorkspaceStatus("not-found", "random", &WorkspaceStatus{Phase: WorkspacePausing})
assert.NotNil(t, err)
userErr, ok := err.(*util.UserError)
assert.True(t, ok)
assert.Equal(t, userErr.Code, codes.NotFound)
}
func Test_UpdateWorkspaceStatus(t *testing.T) {
testUpdateWorkspaceStatusSuccess(t)
testUpdateWorkspaceStatusNotFound(t)
}

View File

@@ -3,6 +3,7 @@ package v1
import (
"fmt"
wfv1 "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1"
uid2 "github.com/onepanelio/core/pkg/util/uid"
"github.com/onepanelio/core/util/sql"
networking "istio.io/api/networking/v1alpha3"
corev1 "k8s.io/api/core/v1"
@@ -63,6 +64,29 @@ func (w *Workspace) GetURL(protocol, domain string) string {
return fmt.Sprintf("%v%v--%v.%v", protocol, w.UID, w.Namespace, domain)
}
// GetParameterValue returns the value of the parameter with the given name, or nil if there is no such parameter
func (w *Workspace) GetParameterValue(name string) *string {
for _, p := range w.Parameters {
if p.Name == name {
return p.Value
}
}
return nil
}
// GenerateUID generates a uid from the input name and sets it on the workspace
func (w *Workspace) GenerateUID(name string) error {
result, err := uid2.GenerateUID(name, 30)
if err != nil {
return err
}
w.UID = result
return nil
}
// getWorkspaceColumns returns all of the columns for workspace modified by alias, destination.
// see formatColumnSelect
func getWorkspaceColumns(aliasAndDestination ...string) []string {

View File

@@ -28,7 +28,7 @@ func assertWorkspaceNameValid(t *testing.T, name string) {
assert.True(t, valid)
}
func TestWorkspaceNameValidation_RegexValid(t *testing.T) {
func Test_WorkspaceNameValidation_RegexValid(t *testing.T) {
assertWorkspaceNameInvalid(t, "600s")
assertWorkspaceNameValid(t, "test-5")

View File

@@ -50,6 +50,10 @@ func getBearerToken(ctx context.Context) (*string, bool) {
}
}
for _, t := range md.Get("onepanel-auth-token") {
return &t, true
}
return nil, false
}
@@ -98,6 +102,7 @@ func IsAuthorized(c *v1.Client, namespace, verb, group, resource, name string) (
// 2. Is there a token? There should be a token for everything except logging in.
func UnaryInterceptor(kubeConfig *v1.Config, db *v1.DB, sysConfig v1.SystemConfig) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
// Check if the provided token is valid. This does not require a token in the header.
if info.FullMethod == "/api.AuthService/IsValidToken" {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {

View File

@@ -105,27 +105,6 @@ func (s *WorkflowTemplateServer) CreateWorkflowTemplateVersion(ctx context.Conte
return req.WorkflowTemplate, nil
}
func (s *WorkflowTemplateServer) UpdateWorkflowTemplateVersion(ctx context.Context, req *api.UpdateWorkflowTemplateVersionRequest) (*api.WorkflowTemplate, error) {
client := getClient(ctx)
allowed, err := auth.IsAuthorized(client, req.Namespace, "update", "argoproj.io", "workflowtemplates", req.WorkflowTemplate.Name)
if err != nil || !allowed {
return nil, err
}
workflowTemplate := &v1.WorkflowTemplate{
UID: req.WorkflowTemplate.Uid,
Name: req.WorkflowTemplate.Name,
Manifest: req.WorkflowTemplate.Manifest,
Version: req.WorkflowTemplate.Version,
}
req.WorkflowTemplate.Uid = workflowTemplate.UID
req.WorkflowTemplate.Name = workflowTemplate.Name
req.WorkflowTemplate.Version = workflowTemplate.Version
return req.WorkflowTemplate, nil
}
func (s *WorkflowTemplateServer) GetWorkflowTemplate(ctx context.Context, req *api.GetWorkflowTemplateRequest) (*api.WorkflowTemplate, error) {
client := getClient(ctx)
allowed, err := auth.IsAuthorized(client, req.Namespace, "get", "argoproj.io", "workflowtemplates", "")

View File

@@ -5,14 +5,20 @@ import (
"github.com/golang/protobuf/ptypes/empty"
"github.com/onepanelio/core/api"
v1 "github.com/onepanelio/core/pkg"
"github.com/onepanelio/core/pkg/util"
"github.com/onepanelio/core/pkg/util/pagination"
"github.com/onepanelio/core/pkg/util/ptr"
"github.com/onepanelio/core/server/auth"
"github.com/onepanelio/core/server/converter"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc/codes"
"time"
)
var reservedWorkspaceNames = map[string]bool{
"modeldb": true,
}
type WorkspaceServer struct{}
func apiWorkspace(wt *v1.Workspace, config v1.SystemConfig) *api.Workspace {
@@ -106,6 +112,10 @@ func (s *WorkspaceServer) CreateWorkspace(ctx context.Context, req *api.CreateWo
})
}
if _, isReserved := reservedWorkspaceNames[workspace.Name]; isReserved {
return nil, util.NewUserError(codes.AlreadyExists, "That name is reserved, choose a different name for the workspace.")
}
workspace, err = client.CreateWorkspace(req.Namespace, workspace)
if err != nil {
return nil, err
@@ -147,6 +157,11 @@ func (s *WorkspaceServer) GetWorkspace(ctx context.Context, req *api.GetWorkspac
return nil, err
}
templateParameters, err = sysConfig.UpdateNodePoolOptions(templateParameters)
if err != nil {
return nil, err
}
apiWorkspace.TemplateParameters = converter.ParametersToAPI(templateParameters)
return apiWorkspace, nil