From 256b989c3adf20c12ecd5dfd2cada81f45ca9529 Mon Sep 17 00:00:00 2001 From: Ravi Suhag Date: Wed, 19 Jul 2023 19:58:12 -0500 Subject: [PATCH] feat: optimus upgrades (#770) --- .github/workflows/publish-latest.yml | 6 +- .github/workflows/verify.yml | 12 +- .golangci.yml | 48 +- .goreleaser.latest.yml | 33 +- .goreleaser.yml | 27 +- Makefile | 11 +- README.md | 26 +- buf.gen.yaml | 4 +- client/cmd/backup/create.go | 31 +- client/cmd/backup/list.go | 32 +- client/cmd/backup/status.go | 30 +- client/cmd/commands.go | 32 +- client/cmd/extension/activate.go | 4 +- client/cmd/extension/clean.go | 6 +- client/cmd/extension/describe.go | 4 +- client/cmd/extension/extension.go | 8 +- client/cmd/extension/install.go | 2 +- client/cmd/extension/rename.go | 4 +- client/cmd/extension/uninstall.go | 6 +- client/cmd/extension/upgrade.go | 4 +- client/cmd/initialize/initialize.go | 10 +- client/cmd/internal/auth/auth.go | 59 + client/cmd/internal/auth/keyring.go | 41 + client/cmd/internal/connection/connection.go | 77 + client/cmd/internal/connection/insecure.go | 34 + client/cmd/internal/connection/secure.go | 95 + .../token_auth.go} | 16 +- .../cmd/internal/connectivity/connectivity.go | 120 - client/cmd/internal/load_client_config.go | 4 +- client/cmd/internal/logger/logger.go | 2 +- client/cmd/internal/logger/printer.go | 4 +- client/cmd/internal/manage_plugins.go | 6 +- .../cmd/internal/progressbar/progress_bar.go | 11 +- client/cmd/internal/survey/backup_create.go | 2 +- client/cmd/internal/survey/initialize.go | 6 +- client/cmd/internal/survey/job.go | 6 +- client/cmd/internal/survey/job_addhook.go | 6 +- client/cmd/internal/survey/job_create.go | 11 +- client/cmd/internal/survey/namespace.go | 4 +- client/cmd/internal/survey/resource_create.go | 4 +- client/cmd/internal/survey/survey.go | 2 +- client/cmd/job/addhook.go | 14 +- client/cmd/job/change_namespace.go | 191 + client/cmd/job/create.go | 14 +- client/cmd/job/export.go | 59 +- client/cmd/job/inspect.go | 30 +- client/cmd/job/job.go | 1 + client/cmd/job/refresh.go | 23 +- client/cmd/job/replace_all.go | 41 +- client/cmd/job/run_input.go | 31 +- client/cmd/job/run_list.go | 26 +- client/cmd/job/validate.go | 30 +- client/cmd/namespace/describe.go | 28 +- client/cmd/namespace/list.go | 31 +- client/cmd/namespace/namespace.go | 8 - client/cmd/namespace/register.go | 42 +- client/cmd/playground/playground.go | 2 +- client/cmd/playground/window/model.go | 37 +- client/cmd/playground/window/window.go | 6 +- client/cmd/plugin/install.go | 8 +- client/cmd/plugin/sync.go | 8 +- client/cmd/plugin/validate.go | 6 +- client/cmd/project/describe.go | 29 +- client/cmd/project/register.go | 43 +- client/cmd/replay/create.go | 86 +- client/cmd/replay/list.go | 133 + client/cmd/replay/replay.go | 2 + client/cmd/replay/status.go | 174 + client/cmd/resource/apply.go | 160 + client/cmd/resource/change_namespace.go | 198 + client/cmd/resource/create.go | 12 +- client/cmd/resource/export.go | 60 +- client/cmd/resource/resource.go | 2 + client/cmd/resource/upload_all.go | 94 +- client/cmd/scheduler/upload_all.go | 32 +- client/cmd/secret/delete.go | 30 +- client/cmd/secret/list.go | 39 +- client/cmd/secret/set.go | 41 +- client/cmd/version/version.go | 47 +- client/extension/default_asset_operator.go | 2 +- .../extension/default_asset_operator_test.go | 2 +- client/extension/default_manifester.go | 2 +- client/extension/default_manifester_test.go | 4 +- client/extension/extension.go | 4 +- client/extension/extension_test.go | 4 +- client/extension/factory/factory.go | 2 +- client/extension/factory/factory_test.go | 6 +- client/extension/internal/activate.go | 2 +- client/extension/internal/activate_test.go | 6 +- client/extension/internal/install.go | 4 +- client/extension/internal/install_test.go | 14 +- client/extension/internal/internal.go | 2 +- client/extension/internal/rename.go | 2 +- client/extension/internal/rename_test.go | 6 +- client/extension/internal/run.go | 2 +- client/extension/internal/run_test.go | 6 +- client/extension/internal/uninstall.go | 2 +- client/extension/internal/uninstall_test.go | 6 +- client/extension/internal/upgrade.go | 4 +- client/extension/internal/upgrade_test.go | 10 +- client/extension/manager.go | 4 +- client/extension/manager_test.go | 8 +- client/extension/mock/client.go | 2 +- client/extension/mock/manifester.go | 2 +- client/extension/provider/github/client.go | 4 +- .../extension/provider/github/client_test.go | 2 +- client/extension/provider/github/model.go | 2 +- client/extension/provider/github/parse.go | 4 +- .../extension/provider/github/parse_test.go | 4 +- client/extension/provider/registry.go | 2 +- client/local/internal/internal.go | 2 +- client/local/model/job_spec.go | 12 +- client/local/model/job_spec_test.go | 6 +- client/local/model/resource_spec.go | 3 +- client/local/model/resource_spec_test.go | 4 +- client/local/specio.go | 2 +- client/local/specio/export_test.go | 4 +- client/local/specio/job_spec_io.go | 6 +- client/local/specio/job_spec_io_test.go | 6 +- client/local/specio/resource_spec_io.go | 7 +- client/local/specio/resource_spec_io_test.go | 7 +- config.sample.yaml | 13 +- config/config_client.go | 6 + config/config_client_test.go | 2 +- config/config_server.go | 13 + config/loader.go | 2 +- config/loader_test.go | 22 +- config/validation_test.go | 2 +- core/event/event.go | 27 + core/event/job.go | 155 + core/event/moderator/handler.go | 47 + core/event/moderator/handler_test.go | 117 + core/event/moderator/worker.go | 84 + core/event/resource.go | 90 + core/event/scheduler.go | 111 + core/job/handler/v1beta1/job.go | 247 +- core/job/handler/v1beta1/job_adapter.go | 18 +- core/job/handler/v1beta1/job_test.go | 228 +- core/job/job.go | 20 +- core/job/job_test.go | 6 +- .../resolver/external_upstream_resolver.go | 18 +- .../external_upstream_resolver_test.go | 12 +- .../resolver/internal_upstream_resolver.go | 12 +- .../internal_upstream_resolver_test.go | 8 +- core/job/resolver/upstream_resolver.go | 20 +- core/job/resolver/upstream_resolver_test.go | 10 +- core/job/resolver/util.go | 2 +- core/job/service/filter/filter_test.go | 2 +- core/job/service/job_service.go | 661 +++- core/job/service/job_service_test.go | 1884 ++++++++-- core/job/service/plugin_service.go | 25 +- core/job/service/plugin_service_test.go | 24 +- core/job/spec.go | 96 +- core/job/spec_test.go | 64 +- core/resource/backup.go | 4 +- core/resource/backup_test.go | 4 +- core/resource/handler/v1beta1/backup.go | 19 +- core/resource/handler/v1beta1/backup_test.go | 10 +- core/resource/handler/v1beta1/resource.go | 170 +- .../resource/handler/v1beta1/resource_test.go | 140 +- core/resource/resource.go | 8 +- core/resource/resource_status.go | 2 +- core/resource/resource_test.go | 4 +- core/resource/service/backup_service.go | 50 +- core/resource/service/backup_service_test.go | 29 +- core/resource/service/resource_manager.go | 46 +- .../resource/service/resource_manager_test.go | 135 +- core/resource/service/resource_service.go | 275 +- .../resource/service/resource_service_test.go | 439 ++- core/resource/status.go | 5 + core/resource/status_test.go | 2 +- core/resource/store.go | 2 +- core/resource/store_test.go | 2 +- core/scheduler/event.go | 6 +- core/scheduler/event_test.go | 4 +- core/scheduler/executor.go | 4 +- core/scheduler/executor_test.go | 2 +- core/scheduler/handler/v1beta1/job_run.go | 50 +- .../scheduler/handler/v1beta1/job_run_test.go | 32 +- core/scheduler/handler/v1beta1/replay.go | 90 +- core/scheduler/handler/v1beta1/replay_test.go | 213 +- core/scheduler/job.go | 19 +- core/scheduler/job_run.go | 23 +- core/scheduler/job_test.go | 18 +- core/scheduler/replay.go | 63 +- core/scheduler/replay_test.go | 24 +- core/scheduler/resolver/priority_resolver.go | 144 - .../resolver/priority_resolver_test.go | 463 --- core/scheduler/resolver/simple_resolver.go | 41 + .../resolver/simple_resolver_test.go | 76 + core/scheduler/service/deployment_service.go | 129 +- .../service/deployment_service_test.go | 208 +- .../service/executor_input_compiler.go | 23 +- .../service/executor_input_compiler_test.go | 29 +- .../service/job_run_asset_compiler.go | 16 +- .../service/job_run_asset_compiler_test.go | 25 +- core/scheduler/service/job_run_service.go | 245 +- .../scheduler/service/job_run_service_test.go | 202 +- .../scheduler/service/notification_service.go | 48 +- .../service/notification_service_test.go | 10 +- core/scheduler/service/replay_manager.go | 15 +- core/scheduler/service/replay_manager_test.go | 10 +- core/scheduler/service/replay_service.go | 62 +- core/scheduler/service/replay_service_test.go | 166 +- core/scheduler/service/replay_validator.go | 43 +- .../service/replay_validator_test.go | 109 +- core/scheduler/service/replay_worker.go | 124 +- core/scheduler/service/replay_worker_test.go | 259 +- core/scheduler/status.go | 22 +- core/scheduler/status_test.go | 69 +- core/tenant/handler/v1beta1/namespace.go | 16 +- core/tenant/handler/v1beta1/namespace_test.go | 8 +- core/tenant/handler/v1beta1/project.go | 13 +- core/tenant/handler/v1beta1/project_test.go | 8 +- core/tenant/handler/v1beta1/secret.go | 39 +- core/tenant/handler/v1beta1/secret_test.go | 10 +- core/tenant/namespace.go | 2 +- core/tenant/namespace_test.go | 2 +- core/tenant/project.go | 4 +- core/tenant/project_test.go | 2 +- core/tenant/secret.go | 2 +- core/tenant/secret_test.go | 2 +- core/tenant/service/namespace_service.go | 2 +- core/tenant/service/namespace_service_test.go | 4 +- core/tenant/service/project_service.go | 2 +- core/tenant/service/project_service_test.go | 4 +- core/tenant/service/secret_service.go | 27 +- core/tenant/service/secret_service_test.go | 51 +- core/tenant/service/tenant_service.go | 18 +- core/tenant/service/tenant_service_test.go | 29 +- core/tenant/tenant.go | 4 +- core/tenant/tenant_test.go | 2 +- dev/Makefile | 6 +- dev/setup.yaml | 2 +- docs/blog/2021-10-02-optimus-launch.md | 2 +- docs/docs/building-plugin/introduction.md | 99 + docs/docs/building-plugin/tutorial.md | 258 ++ .../applying-job-specifications.md | 43 + .../client-guide/backup-bigquery-resource.md | 43 + docs/docs/client-guide/configuration.md | 73 + .../client-guide/create-job-specifications.md | 242 ++ docs/docs/client-guide/installing-plugin.md | 10 + .../client-guide/manage-bigquery-resource.md | 112 + .../managing-project-namespace.md | 16 + docs/docs/client-guide/managing-secrets.md | 60 + .../client-guide/organizing-specifications.md | 98 + docs/docs/client-guide/replay-a-job.md | 37 + docs/docs/client-guide/setting-up-alert.md | 35 + .../uploading-jobs-to-scheduler.md | 13 + docs/docs/client-guide/verifying-jobs.md | 41 + docs/docs/client-guide/work-with-extension.md | 178 + docs/docs/concepts/architecture.md | 88 +- docs/docs/concepts/dependency.md | 16 + docs/docs/concepts/intervals-and-windows.md | 103 +- docs/docs/concepts/job-run.md | 7 + docs/docs/concepts/job.md | 101 + docs/docs/concepts/macros.md | 14 + docs/docs/concepts/namespace.md | 5 + docs/docs/concepts/overview.md | 513 --- docs/docs/concepts/plugin.md | 7 + docs/docs/concepts/project.md | 4 + docs/docs/concepts/replay-and-backup.md | 21 + docs/docs/concepts/resource.md | 14 + docs/docs/concepts/secret.md | 9 + docs/docs/contribute/contributing.md | 37 - docs/docs/contribute/contribution-process.md | 34 + docs/docs/contribute/developer-env-setup.md | 4 + docs/docs/development/building-plugin.md | 953 ----- docs/docs/getting-started/configuration.md | 124 - docs/docs/getting-started/installation.md | 62 +- docs/docs/getting-started/quick-start.md | 344 ++ docs/docs/guides/adding-hook.md | 68 - docs/docs/guides/alerts.md | 24 - docs/docs/guides/backup.md | 66 - docs/docs/guides/create-bigquery-dataset.md | 90 - .../guides/create-bigquery-external-table.md | 161 - docs/docs/guides/create-bigquery-table.md | 178 - docs/docs/guides/create-bigquery-view.md | 103 - docs/docs/guides/create-job.md | 122 - docs/docs/guides/extension.md | 214 -- docs/docs/guides/manage-secrets.md | 57 - docs/docs/guides/optimus-serve.md | 29 - docs/docs/guides/organising-specifcations.md | 133 - docs/docs/guides/predator.md | 78 - .../publishing-from-bigquery-to-kafka.md | 92 - docs/docs/guides/refresh-jobs.md | 34 - docs/docs/guides/replay.md | 55 - docs/docs/guides/task-bq2bq.md | 171 - docs/docs/introduction.md | 73 +- docs/docs/reference/API.md | 9 +- docs/docs/reference/FAQ.md | 35 +- docs/docs/reference/metrics.md | 50 + docs/docs/reference/shell-autocompletion.md | 112 - .../20220507_simplify_plugin_maintenance.md | 121 +- docs/docs/roadmap.md | 45 - docs/docs/server-guide/configuration.md | 20 + docs/docs/server-guide/db-migrations.md | 34 + docs/docs/server-guide/installing-plugins.md | 31 + .../server-guide/starting-optimus-server.md | 54 + docs/docusaurus.config.js | 24 +- docs/sidebars.js | 79 +- docs/src/pages/help.js | 6 +- docs/src/pages/index.js | 2 +- docs/static/img/banner-0.6.png | Bin 0 -> 267500 bytes docs/static/img/docs/Concept_JobRun.png | Bin 0 -> 130268 bytes docs/static/img/docs/CreateJobSpecFlow.png | Bin 0 -> 133519 bytes docs/static/img/docs/OptimusArchitecture.png | Bin 0 -> 203665 bytes .../OptimusArchitecture_dark_07June2021.png | Bin 403725 -> 0 bytes docs/static/img/docs/OptimusIntro.png | Bin 0 -> 92067 bytes docs/static/img/docs/ReplayParallel.png | Bin 0 -> 19842 bytes docs/static/img/docs/ReplaySequential.png | Bin 0 -> 27206 bytes ext/notify/notify.go | 4 +- ext/notify/pagerduty/pagerdutynotifier.go | 15 +- .../pagerduty/pagerdutynotifier_test.go | 6 +- ext/notify/slack/slack.go | 15 +- ext/notify/slack/slack_test.go | 4 +- ext/resourcemanager/model.go | 1 - ext/resourcemanager/optimus.go | 6 +- ext/resourcemanager/optimus_test.go | 8 +- ext/scheduler/airflow/__lib.py | 72 +- ext/scheduler/airflow/airflow.go | 144 +- ext/scheduler/airflow/bucket/factory.go | 6 +- ext/scheduler/airflow/bucket/gcs.go | 4 +- ext/scheduler/airflow/client.go | 38 +- ext/scheduler/airflow/dag/compiler.go | 12 +- ext/scheduler/airflow/dag/compiler_test.go | 62 +- ext/scheduler/airflow/dag/dag.py.tmpl | 43 +- ext/scheduler/airflow/dag/expected_dag.py | 39 +- ext/scheduler/airflow/dag/models.go | 8 +- .../airflow/dag/template_func_test.go | 2 +- ext/scheduler/airflow/dag/upstream.go | 4 +- ext/store/bigquery/backup.go | 6 +- ext/store/bigquery/backup_test.go | 8 +- ext/store/bigquery/batch.go | 8 +- ext/store/bigquery/batch_test.go | 8 +- ext/store/bigquery/bigquery.go | 8 +- ext/store/bigquery/bigquery_test.go | 6 +- ext/store/bigquery/client.go | 2 +- ext/store/bigquery/client_test.go | 4 +- ext/store/bigquery/copier.go | 2 +- ext/store/bigquery/copier_test.go | 4 +- ext/store/bigquery/dataset.go | 4 +- ext/store/bigquery/dataset_spec.go | 4 +- ext/store/bigquery/dataset_spec_test.go | 6 +- ext/store/bigquery/dataset_test.go | 6 +- ext/store/bigquery/external_table.go | 23 +- ext/store/bigquery/external_table_spec.go | 4 +- .../bigquery/external_table_spec_test.go | 2 +- ext/store/bigquery/external_table_test.go | 8 +- ext/store/bigquery/job.go | 2 +- ext/store/bigquery/job_test.go | 4 +- ext/store/bigquery/schema.go | 2 +- ext/store/bigquery/schema_test.go | 2 +- ext/store/bigquery/table.go | 4 +- ext/store/bigquery/table_spec.go | 4 +- ext/store/bigquery/table_spec_test.go | 2 +- ext/store/bigquery/table_test.go | 6 +- ext/store/bigquery/view.go | 4 +- ext/store/bigquery/view_spec.go | 4 +- ext/store/bigquery/view_spec_test.go | 2 +- ext/store/bigquery/view_test.go | 6 +- ext/transport/kafka/writer.go | 60 + go.mod | 32 +- go.sum | 150 +- internal/compiler/engine.go | 9 +- internal/compiler/engine_test.go | 9 +- internal/compiler/template_context.go | 2 +- internal/compiler/template_context_test.go | 2 +- internal/compiler/template_func_test.go | 2 +- internal/errors/errors.go | 10 +- internal/errors/errors_test.go | 2 +- internal/errors/multi_test.go | 8 +- internal/lib/cron/cron_test.go | 2 +- internal/lib/progress/progress.go | 32 - internal/lib/set/set.go | 59 - internal/lib/tree/multi_root_tree_test.go | 2 +- internal/lib/tree/tree_node.go | 6 - internal/lib/tree/tree_node_test.go | 2 +- internal/models/plugin.go | 2 +- internal/models/plugin_test.go | 6 +- internal/models/window_test.go | 2 +- internal/models/window_v1_test.go | 2 +- internal/models/window_v2.go | 2 +- internal/models/window_v2_test.go | 2 +- internal/store/postgres/job/adapter.go | 9 +- internal/store/postgres/job/job_repository.go | 197 +- .../store/postgres/job/job_repository_test.go | 149 +- .../000053_cleanup_old_data.down.sql | 170 + .../migrations/000053_cleanup_old_data.up.sql | 13 + .../000054_job_run_constraint.down.sql | 1 + .../000054_job_run_constraint.up.sql | 10 + .../000055_job_enable_status.down.sql | 3 + .../000055_job_enable_status.up.sql | 7 + internal/store/postgres/pgx.go | 2 +- .../postgres/resource/backup_repository.go | 6 +- .../resource/backup_repository_test.go | 6 +- .../store/postgres/resource/repository.go | 29 +- .../postgres/resource/repository_test.go | 60 +- internal/store/postgres/resource/resource.go | 6 +- .../scheduler/job_operator_repository.go | 5 +- .../scheduler/job_operator_repository_test.go | 8 +- .../postgres/scheduler/job_repository.go | 116 +- .../postgres/scheduler/job_repository_test.go | 60 +- .../postgres/scheduler/job_run_repository.go | 36 +- .../scheduler/job_run_repository_test.go | 6 +- .../postgres/scheduler/replay_repository.go | 102 +- .../scheduler/replay_repository_test.go | 96 +- .../postgres/tenant/namespace_repository.go | 4 +- .../tenant/namespace_repository_test.go | 6 +- .../postgres/tenant/project_repository.go | 4 +- .../tenant/project_repository_test.go | 6 +- .../postgres/tenant/secret_repository.go | 6 +- .../postgres/tenant/secret_repository_test.go | 6 +- internal/telemetry/dashboards/Readme.md | 3 + internal/telemetry/dashboards/grafana8.json | 1285 ------- .../scripts/add_monitoring_role.down.sql | 5 + .../scripts/add_monitoring_role.up.sql | 9 + internal/telemetry/prometheus.go | 30 +- internal/telemetry/telemetry.go | 4 +- internal/utils/contains_test.go | 2 +- internal/utils/convert_test.go | 2 +- internal/utils/file_test.go | 2 +- internal/utils/map_test.go | 2 +- internal/utils/proto_test.go | 2 +- internal/utils/validator_test.go | 2 +- ...check_job_specification_response_writer.go | 2 +- ..._resource_specification_response_writer.go | 2 +- internal/writer/logwriter.go | 4 +- .../writer/refresh_job_response_writer.go | 2 +- ..._all_job_specifications_response_writer.go | 2 +- internal/writer/writer.go | 2 +- main.go | 8 +- plugin/binary/plugin.go | 6 +- plugin/plugin.go | 10 +- plugin/plugin_manager.go | 4 +- plugin/v1beta1/dependencyresolver/adapter.go | 4 +- plugin/v1beta1/dependencyresolver/client.go | 12 +- .../v1beta1/dependencyresolver/connector.go | 4 +- plugin/v1beta1/dependencyresolver/server.go | 7 +- plugin/yaml/plugin.go | 4 +- plugin/yaml/plugin_test.go | 8 +- plugin/yaml/tests/sample_plugin.yaml | 8 +- .../tests/sample_plugin_without_shell.yaml | 8 +- .../optimus/cluster/v1beta1/command.pb.go | 511 --- protos/odpf/optimus/core/v1beta1/backup.pb.go | 859 ----- .../odpf/optimus/core/v1beta1/job_run.pb.go | 1268 ------- .../odpf/optimus/core/v1beta1/namespace.pb.go | 649 ---- .../odpf/optimus/core/v1beta1/project.pb.go | 691 ---- protos/odpf/optimus/core/v1beta1/replay.pb.go | 314 -- .../odpf/optimus/core/v1beta1/replay.pb.gw.go | 201 - .../optimus/core/v1beta1/replay.swagger.json | 131 - .../optimus/core/v1beta1/replay_grpc.pb.go | 105 - .../odpf/optimus/core/v1beta1/resource.pb.go | 1167 ------ .../odpf/optimus/core/v1beta1/runtime.pb.go | 234 -- protos/odpf/optimus/core/v1beta1/secret.pb.go | 809 ---- protos/odpf/optimus/core/v1beta1/status.pb.go | 232 -- .../plugins/v1beta1/dependency_resolver.pb.go | 1182 ------ .../optimus/core/v1beta1/backup.pb.go | 865 +++++ .../optimus/core/v1beta1/backup.pb.gw.go | 14 +- .../optimus/core/v1beta1/backup.swagger.json | 26 +- .../optimus/core/v1beta1/backup_grpc.pb.go | 18 +- .../optimus/core/v1beta1/job_run.pb.go | 1277 +++++++ .../optimus/core/v1beta1/job_run.pb.gw.go | 18 +- .../optimus/core/v1beta1/job_run.swagger.json | 40 +- .../optimus/core/v1beta1/job_run_grpc.pb.go | 22 +- .../optimus/core/v1beta1/job_spec.pb.go | 3306 ++++++++++------- .../optimus/core/v1beta1/job_spec.pb.gw.go | 431 ++- .../core/v1beta1/job_spec.swagger.json | 243 +- .../optimus/core/v1beta1/job_spec_grpc.pb.go | 176 +- .../optimus/core/v1beta1/namespace.pb.go | 655 ++++ .../optimus/core/v1beta1/namespace.pb.gw.go | 14 +- .../core/v1beta1/namespace.swagger.json | 26 +- .../optimus/core/v1beta1/namespace_grpc.pb.go | 18 +- .../optimus/core/v1beta1/project.pb.go | 697 ++++ .../optimus/core/v1beta1/project.pb.gw.go | 14 +- .../optimus/core/v1beta1/project.swagger.json | 26 +- .../optimus/core/v1beta1/project_grpc.pb.go | 18 +- .../optimus/core/v1beta1/replay.pb.go | 1069 ++++++ .../optimus/core/v1beta1/replay.pb.gw.go | 534 +++ .../optimus/core/v1beta1/replay.swagger.json | 326 ++ .../optimus/core/v1beta1/replay_grpc.pb.go | 213 ++ .../optimus/core/v1beta1/resource.pb.go | 1616 ++++++++ .../optimus/core/v1beta1/resource.pb.gw.go | 288 +- .../core/v1beta1/resource.swagger.json | 170 +- .../optimus/core/v1beta1/resource_grpc.pb.go | 100 +- .../optimus/core/v1beta1/runtime.pb.go | 236 ++ .../optimus/core/v1beta1/runtime.pb.gw.go | 6 +- .../optimus/core/v1beta1/runtime.swagger.json | 18 +- .../optimus/core/v1beta1/runtime_grpc.pb.go | 10 +- .../optimus/core/v1beta1/secret.pb.go | 814 ++++ .../optimus/core/v1beta1/secret.pb.gw.go | 18 +- .../optimus/core/v1beta1/secret.swagger.json | 30 +- .../optimus/core/v1beta1/secret_grpc.pb.go | 22 +- .../optimus/core/v1beta1/status.pb.go | 234 ++ .../optimus/core/v1beta1/status.swagger.json | 10 +- .../optimus/integration/v1beta1/event.pb.go | 746 ++++ .../integration/v1beta1/event.swagger.json} | 10 +- .../plugins/v1beta1/dependency_resolver.pb.go | 1204 ++++++ .../v1beta1/dependency_resolver.swagger.json | 69 +- .../v1beta1/dependency_resolver_grpc.pb.go | 22 +- sdk/go.mod | 2 +- sdk/plugin/mock/mods.go | 2 +- sdk/plugin/mock/plugin.go | 2 +- sdk/plugin/plugin_test.go | 14 +- server/cmd/migration/migrate_to.go | 4 +- server/cmd/migration/rollback.go | 4 +- server/cmd/serve.go | 6 +- server/handler/v1beta1/version.go | 4 +- server/handler/v1beta1/version_test.go | 6 +- server/logger.go | 2 +- server/optimus.go | 144 +- server/scheduler.go | 10 +- server/server.go | 8 +- tests/bench/job/job_repo_test.go | 10 +- tests/bench/resource/backup_repo_test.go | 10 +- tests/bench/resource/resource_repo_test.go | 12 +- tests/bench/scheduler/job_repo_test.go | 14 +- tests/bench/scheduler/job_run_repo_test.go | 14 +- .../bench/scheduler/operator_run_repo_test.go | 14 +- tests/bench/tenant/namespace_repo_test.go | 6 +- tests/bench/tenant/project_repo_test.go | 6 +- tests/bench/tenant/secret_repo_test.go | 6 +- tests/setup/database.go | 4 +- tests/setup/job.go | 6 +- tests/setup/plugin.go | 2 +- 525 files changed, 27064 insertions(+), 18679 deletions(-) create mode 100644 client/cmd/internal/auth/auth.go create mode 100644 client/cmd/internal/auth/keyring.go create mode 100644 client/cmd/internal/connection/connection.go create mode 100644 client/cmd/internal/connection/insecure.go create mode 100644 client/cmd/internal/connection/secure.go rename client/cmd/internal/{connectivity/authentication.go => connection/token_auth.go} (50%) delete mode 100644 client/cmd/internal/connectivity/connectivity.go create mode 100644 client/cmd/job/change_namespace.go create mode 100644 client/cmd/replay/list.go create mode 100644 client/cmd/replay/status.go create mode 100644 client/cmd/resource/apply.go create mode 100644 client/cmd/resource/change_namespace.go create mode 100644 core/event/event.go create mode 100644 core/event/job.go create mode 100644 core/event/moderator/handler.go create mode 100644 core/event/moderator/handler_test.go create mode 100644 core/event/moderator/worker.go create mode 100644 core/event/resource.go create mode 100644 core/event/scheduler.go delete mode 100644 core/scheduler/resolver/priority_resolver.go delete mode 100644 core/scheduler/resolver/priority_resolver_test.go create mode 100644 core/scheduler/resolver/simple_resolver.go create mode 100644 core/scheduler/resolver/simple_resolver_test.go create mode 100644 docs/docs/building-plugin/introduction.md create mode 100644 docs/docs/building-plugin/tutorial.md create mode 100644 docs/docs/client-guide/applying-job-specifications.md create mode 100644 docs/docs/client-guide/backup-bigquery-resource.md create mode 100644 docs/docs/client-guide/configuration.md create mode 100644 docs/docs/client-guide/create-job-specifications.md create mode 100644 docs/docs/client-guide/installing-plugin.md create mode 100644 docs/docs/client-guide/manage-bigquery-resource.md create mode 100644 docs/docs/client-guide/managing-project-namespace.md create mode 100644 docs/docs/client-guide/managing-secrets.md create mode 100644 docs/docs/client-guide/organizing-specifications.md create mode 100644 docs/docs/client-guide/replay-a-job.md create mode 100644 docs/docs/client-guide/setting-up-alert.md create mode 100644 docs/docs/client-guide/uploading-jobs-to-scheduler.md create mode 100644 docs/docs/client-guide/verifying-jobs.md create mode 100644 docs/docs/client-guide/work-with-extension.md create mode 100644 docs/docs/concepts/dependency.md create mode 100644 docs/docs/concepts/job-run.md create mode 100644 docs/docs/concepts/job.md create mode 100644 docs/docs/concepts/macros.md create mode 100644 docs/docs/concepts/namespace.md delete mode 100644 docs/docs/concepts/overview.md create mode 100644 docs/docs/concepts/plugin.md create mode 100644 docs/docs/concepts/project.md create mode 100644 docs/docs/concepts/replay-and-backup.md create mode 100644 docs/docs/concepts/resource.md create mode 100644 docs/docs/concepts/secret.md delete mode 100644 docs/docs/contribute/contributing.md create mode 100644 docs/docs/contribute/contribution-process.md create mode 100644 docs/docs/contribute/developer-env-setup.md delete mode 100644 docs/docs/development/building-plugin.md delete mode 100644 docs/docs/getting-started/configuration.md create mode 100644 docs/docs/getting-started/quick-start.md delete mode 100644 docs/docs/guides/adding-hook.md delete mode 100644 docs/docs/guides/alerts.md delete mode 100644 docs/docs/guides/backup.md delete mode 100644 docs/docs/guides/create-bigquery-dataset.md delete mode 100644 docs/docs/guides/create-bigquery-external-table.md delete mode 100644 docs/docs/guides/create-bigquery-table.md delete mode 100644 docs/docs/guides/create-bigquery-view.md delete mode 100644 docs/docs/guides/create-job.md delete mode 100644 docs/docs/guides/extension.md delete mode 100644 docs/docs/guides/manage-secrets.md delete mode 100644 docs/docs/guides/optimus-serve.md delete mode 100644 docs/docs/guides/organising-specifcations.md delete mode 100644 docs/docs/guides/predator.md delete mode 100644 docs/docs/guides/publishing-from-bigquery-to-kafka.md delete mode 100644 docs/docs/guides/refresh-jobs.md delete mode 100644 docs/docs/guides/replay.md delete mode 100644 docs/docs/guides/task-bq2bq.md create mode 100644 docs/docs/reference/metrics.md delete mode 100644 docs/docs/reference/shell-autocompletion.md delete mode 100644 docs/docs/roadmap.md create mode 100644 docs/docs/server-guide/configuration.md create mode 100644 docs/docs/server-guide/db-migrations.md create mode 100644 docs/docs/server-guide/installing-plugins.md create mode 100644 docs/docs/server-guide/starting-optimus-server.md create mode 100644 docs/static/img/banner-0.6.png create mode 100644 docs/static/img/docs/Concept_JobRun.png create mode 100644 docs/static/img/docs/CreateJobSpecFlow.png create mode 100644 docs/static/img/docs/OptimusArchitecture.png delete mode 100644 docs/static/img/docs/OptimusArchitecture_dark_07June2021.png create mode 100644 docs/static/img/docs/OptimusIntro.png create mode 100644 docs/static/img/docs/ReplayParallel.png create mode 100644 docs/static/img/docs/ReplaySequential.png create mode 100644 ext/transport/kafka/writer.go delete mode 100644 internal/lib/progress/progress.go delete mode 100644 internal/lib/set/set.go create mode 100644 internal/store/postgres/migrations/000053_cleanup_old_data.down.sql create mode 100644 internal/store/postgres/migrations/000053_cleanup_old_data.up.sql create mode 100644 internal/store/postgres/migrations/000054_job_run_constraint.down.sql create mode 100644 internal/store/postgres/migrations/000054_job_run_constraint.up.sql create mode 100644 internal/store/postgres/migrations/000055_job_enable_status.down.sql create mode 100644 internal/store/postgres/migrations/000055_job_enable_status.up.sql create mode 100644 internal/telemetry/dashboards/Readme.md delete mode 100644 internal/telemetry/dashboards/grafana8.json create mode 100644 internal/telemetry/dashboards/scripts/add_monitoring_role.down.sql create mode 100644 internal/telemetry/dashboards/scripts/add_monitoring_role.up.sql delete mode 100644 protos/odpf/optimus/cluster/v1beta1/command.pb.go delete mode 100644 protos/odpf/optimus/core/v1beta1/backup.pb.go delete mode 100644 protos/odpf/optimus/core/v1beta1/job_run.pb.go delete mode 100644 protos/odpf/optimus/core/v1beta1/namespace.pb.go delete mode 100644 protos/odpf/optimus/core/v1beta1/project.pb.go delete mode 100644 protos/odpf/optimus/core/v1beta1/replay.pb.go delete mode 100644 protos/odpf/optimus/core/v1beta1/replay.pb.gw.go delete mode 100644 protos/odpf/optimus/core/v1beta1/replay.swagger.json delete mode 100644 protos/odpf/optimus/core/v1beta1/replay_grpc.pb.go delete mode 100644 protos/odpf/optimus/core/v1beta1/resource.pb.go delete mode 100644 protos/odpf/optimus/core/v1beta1/runtime.pb.go delete mode 100644 protos/odpf/optimus/core/v1beta1/secret.pb.go delete mode 100644 protos/odpf/optimus/core/v1beta1/status.pb.go delete mode 100644 protos/odpf/optimus/plugins/v1beta1/dependency_resolver.pb.go create mode 100644 protos/raystack/optimus/core/v1beta1/backup.pb.go rename protos/{odpf => raystack}/optimus/core/v1beta1/backup.pb.gw.go (93%) rename protos/{odpf => raystack}/optimus/core/v1beta1/backup.swagger.json (94%) rename protos/{odpf => raystack}/optimus/core/v1beta1/backup_grpc.pb.go (89%) create mode 100644 protos/raystack/optimus/core/v1beta1/job_run.pb.go rename protos/{odpf => raystack}/optimus/core/v1beta1/job_run.pb.gw.go (94%) rename protos/{odpf => raystack}/optimus/core/v1beta1/job_run.swagger.json (94%) rename protos/{odpf => raystack}/optimus/core/v1beta1/job_run_grpc.pb.go (90%) rename protos/{odpf => raystack}/optimus/core/v1beta1/job_spec.pb.go (51%) rename protos/{odpf => raystack}/optimus/core/v1beta1/job_spec.pb.gw.go (74%) rename protos/{odpf => raystack}/optimus/core/v1beta1/job_spec.swagger.json (83%) rename protos/{odpf => raystack}/optimus/core/v1beta1/job_spec_grpc.pb.go (79%) create mode 100644 protos/raystack/optimus/core/v1beta1/namespace.pb.go rename protos/{odpf => raystack}/optimus/core/v1beta1/namespace.pb.gw.go (93%) rename protos/{odpf => raystack}/optimus/core/v1beta1/namespace.swagger.json (93%) rename protos/{odpf => raystack}/optimus/core/v1beta1/namespace_grpc.pb.go (90%) create mode 100644 protos/raystack/optimus/core/v1beta1/project.pb.go rename protos/{odpf => raystack}/optimus/core/v1beta1/project.pb.gw.go (93%) rename protos/{odpf => raystack}/optimus/core/v1beta1/project.swagger.json (93%) rename protos/{odpf => raystack}/optimus/core/v1beta1/project_grpc.pb.go (90%) create mode 100644 protos/raystack/optimus/core/v1beta1/replay.pb.go create mode 100644 protos/raystack/optimus/core/v1beta1/replay.pb.gw.go create mode 100644 protos/raystack/optimus/core/v1beta1/replay.swagger.json create mode 100644 protos/raystack/optimus/core/v1beta1/replay_grpc.pb.go create mode 100644 protos/raystack/optimus/core/v1beta1/resource.pb.go rename protos/{odpf => raystack}/optimus/core/v1beta1/resource.pb.gw.go (66%) rename protos/{odpf => raystack}/optimus/core/v1beta1/resource.swagger.json (69%) rename protos/{odpf => raystack}/optimus/core/v1beta1/resource_grpc.pb.go (71%) create mode 100644 protos/raystack/optimus/core/v1beta1/runtime.pb.go rename protos/{odpf => raystack}/optimus/core/v1beta1/runtime.pb.gw.go (95%) rename protos/{odpf => raystack}/optimus/core/v1beta1/runtime.swagger.json (89%) rename protos/{odpf => raystack}/optimus/core/v1beta1/runtime_grpc.pb.go (91%) create mode 100644 protos/raystack/optimus/core/v1beta1/secret.pb.go rename protos/{odpf => raystack}/optimus/core/v1beta1/secret.pb.gw.go (94%) rename protos/{odpf => raystack}/optimus/core/v1beta1/secret.swagger.json (94%) rename protos/{odpf => raystack}/optimus/core/v1beta1/secret_grpc.pb.go (90%) create mode 100644 protos/raystack/optimus/core/v1beta1/status.pb.go rename protos/{odpf => raystack}/optimus/core/v1beta1/status.swagger.json (83%) create mode 100644 protos/raystack/optimus/integration/v1beta1/event.pb.go rename protos/{odpf/optimus/cluster/v1beta1/command.swagger.json => raystack/optimus/integration/v1beta1/event.swagger.json} (82%) create mode 100644 protos/raystack/optimus/plugins/v1beta1/dependency_resolver.pb.go rename protos/{odpf => raystack}/optimus/plugins/v1beta1/dependency_resolver.swagger.json (66%) rename protos/{odpf => raystack}/optimus/plugins/v1beta1/dependency_resolver_grpc.pb.go (89%) diff --git a/.github/workflows/publish-latest.yml b/.github/workflows/publish-latest.yml index 3e41fd5a4d..e4c7c474c1 100644 --- a/.github/workflows/publish-latest.yml +++ b/.github/workflows/publish-latest.yml @@ -28,10 +28,10 @@ jobs: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v2.6.1 + uses: goreleaser/goreleaser-action@v4 with: distribution: goreleaser - version: v1.8.3 + version: v1.19 args: -f .goreleaser.latest.yml --rm-dist --skip-validate env: GITHUB_TOKEN: ${{ secrets.GO_RELEASER_TOKEN }} @@ -59,4 +59,4 @@ jobs: run: | git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" git config --global user.name "github-actions[bot]" - yarn deploy + yarn deploy \ No newline at end of file diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index b0c6713149..6b9a77ed78 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -33,12 +33,12 @@ jobs: go-version: '1.19' - name: test binaries run: make test-ci - - name: Install goveralls - run: go install github.com/mattn/goveralls@latest - - name: Send coverage - env: - COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: goveralls -coverprofile=coverage.txt -service=github + # - name: Install goveralls + # run: go install github.com/mattn/goveralls@latest + # - name: Send coverage + # env: + # COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # run: goveralls -coverprofile=coverage.txt -service=github integration-test: runs-on: ubuntu-latest services: diff --git a/.golangci.yml b/.golangci.yml index 61b214484a..40360a3bb5 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -60,30 +60,30 @@ linters-settings: rules: - name: atomic - name: context-as-argument -# - name: context-keys-type #perf issue + # - name: context-keys-type #perf issue - name: defer - name: dot-imports - name: empty-block - name: error-naming - name: error-return -# - name: error-strings + # - name: error-strings - name: early-return -# - name: errorf #perf issue -# - name: exported + # - name: errorf #perf issue + # - name: exported - name: if-return - name: increment-decrement - name: indent-error-flow -# - name: flag-parameter + # - name: flag-parameter - name: modifies-parameter -# - name: modifies-value-receiver #perf issue + # - name: modifies-value-receiver #perf issue - name: package-comments - name: range - name: receiver-naming - name: redefines-builtin-id - name: superfluous-else -# - name: time-naming #perf issue + # - name: time-naming #perf issue - name: unexported-naming -# - name: var-declaration #perf issue + # - name: var-declaration #perf issue - name: var-naming - name: unused-receiver - name: unused-parameter @@ -95,18 +95,18 @@ linters-settings: numbers: true gomnd: ignored-numbers: # Why we have a big range of file permissions - - '0o600' - - '0o644' - - '0o655' - - '0o666' - - '0o770' - - '0o755' - - '0o765' - - '0o777' + - "0o600" + - "0o644" + - "0o655" + - "0o666" + - "0o770" + - "0o755" + - "0o765" + - "0o777" ignored-functions: - - 'survey.MinLength' - - 'survey.MaxLength' - - 'args.Error' + - "survey.MinLength" + - "survey.MaxLength" + - "args.Error" gosec: excludes: - G101 @@ -121,20 +121,20 @@ linters-settings: - "all" - "-SA1019" goimports: - local-prefixes: github.com/odpf/optimus + local-prefixes: github.com/raystack/optimus gci: sections: - standard # Captures all standard packages if they do not match another section. - default # Contains all imports that could not be matched to another section type. - - prefix(github.com/odpf/optimus) # Groups all imports with the specified Prefix. + - prefix(github.com/raystack/optimus) # Groups all imports with the specified Prefix. gocritic: disabled-checks: - ifElseChain - singleCaseSwitch enabled-tags: - diagnostic -# - style -# - opinionated + # - style + # - opinionated - performance unparam: # Inspect exported functions. @@ -154,4 +154,4 @@ issues: - unparam - testpackage severity: - default-severity: error \ No newline at end of file + default-severity: error diff --git a/.goreleaser.latest.yml b/.goreleaser.latest.yml index e0e8b41ae1..2503877bd6 100644 --- a/.goreleaser.latest.yml +++ b/.goreleaser.latest.yml @@ -10,7 +10,7 @@ builds: flags: - -a ldflags: - - -s -w -X github.com/odpf/optimus/config.BuildVersion=latest -X github.com/odpf/optimus/config.BuildCommit={{.FullCommit}} -X github.com/odpf/optimus/config.BuildDate={{.Date}} + - -s -w -X github.com/raystack/optimus/config.BuildVersion=latest -X github.com/raystack/optimus/config.BuildCommit={{.FullCommit}} -X github.com/raystack/optimus/config.BuildDate={{.Date}} goos: - linux - darwin @@ -21,12 +21,12 @@ builds: env: - CGO_ENABLED=0 archives: - - name_template: "{{ .ProjectName }}_latest_{{ .Os }}_{{ .Arch }}" - replacements: - darwin: macos - linux: linux - windows: windows - amd64: x86_64 + - name_template: >- + {{ .ProjectName }}_latest_ + {{- if eq .Os "darwin" }}macos + {{- else }}{{ .Os }}{{ end }}_ + {{- if eq .Arch "amd64" }}x86_64 + {{- else }}{{ .Arch }}{{ end }} format_overrides: - goos: windows format: zip @@ -34,26 +34,25 @@ release: disable: true name_template: "{{.ProjectName}}-latest" checksum: - name_template: 'checksums.txt' + name_template: "checksums.txt" snapshot: name_template: "latest" changelog: sort: asc filters: exclude: - - '^docs' - - '^test' - - '^build' - - '^chore' + - "^docs" + - "^test" + - "^build" + - "^chore" dockers: - - - goos: linux + - goos: linux goarch: amd64 ids: - optimus dockerfile: Dockerfile image_templates: - - 'docker.io/odpf/{{.ProjectName}}:latest' - - 'docker.io/odpf/{{.ProjectName}}:latest-amd64' + - "docker.io/raystack/{{.ProjectName}}:latest" + - "docker.io/raystack/{{.ProjectName}}:latest-amd64" extra_files: - - entrypoint_init_container.sh \ No newline at end of file + - entrypoint_init_container.sh diff --git a/.goreleaser.yml b/.goreleaser.yml index 395d412e23..7d7394b2d5 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -10,7 +10,7 @@ builds: flags: - -a ldflags: - - -s -w -X github.com/odpf/optimus/config.BuildVersion={{ .Version }} -X github.com/odpf/optimus/config.BuildCommit={{.FullCommit}} -X github.com/odpf/optimus/config.BuildDate={{.Date}} + - -s -w -X github.com/raystack/optimus/config.BuildVersion={{ .Version }} -X github.com/raystack/optimus/config.BuildCommit={{.FullCommit}} -X github.com/raystack/optimus/config.BuildDate={{.Date}} goos: - linux - darwin @@ -33,37 +33,36 @@ release: draft: true prerelease: auto checksum: - name_template: 'checksums.txt' + name_template: "checksums.txt" snapshot: name_template: "{{ .Tag }}-next" changelog: sort: asc filters: exclude: - - '^docs' - - '^test' - - '^build' - - '^chore' + - "^docs" + - "^test" + - "^build" + - "^chore" dockers: - - - goos: linux + - goos: linux goarch: amd64 ids: - optimus dockerfile: Dockerfile image_templates: - - 'docker.io/odpf/{{.ProjectName}}:{{ .Version }}' - - 'docker.io/odpf/{{.ProjectName}}:{{ .Version }}-amd64' + - "docker.io/raystack/{{.ProjectName}}:{{ .Version }}" + - "docker.io/raystack/{{.ProjectName}}:{{ .Version }}-amd64" extra_files: - entrypoint_init_container.sh brews: - name: optimus tap: - owner: odpf + owner: raystack name: homebrew-tap license: "Apache 2.0" description: "Optimus helps your organization to build & manage data pipelines with ease." - homepage: https://odpf.github.io/optimus + homepage: https://raystack.github.io/optimus folder: Formula skip_upload: auto dependencies: @@ -71,5 +70,5 @@ brews: install: |- bin.install "optimus" commit_author: - name: github-actions[bot] - email: 41898282+github-actions[bot]@users.noreply.github.com \ No newline at end of file + name: Ravi Suhag + email: suhag.ravi@gmail.com diff --git a/Makefile b/Makefile index 09936e02db..4990784960 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,11 @@ .ONESHELL: .DELETE_ON_ERROR: MAKEFLAGS += --no-builtin-rules -NAME = "github.com/odpf/optimus" +NAME = "github.com/raystack/optimus" LAST_COMMIT := $(shell git rev-parse --short HEAD) LAST_TAG := "$(shell git rev-list --tags --max-count=1)" OPMS_VERSION := "$(shell git describe --tags ${LAST_TAG})-next" -PROTON_COMMIT := "31ac9046d1a8c95a2f4645b87bf0620a3e6bb8bc" +PROTON_COMMIT := "1c39e65e529d573a1cd422e44f019c62d65fd10b" .PHONY: build test test-ci generate-proto unit-test-ci integration-test vet coverage clean install lint @@ -30,9 +30,9 @@ scheduler-resource-test: cd ./ext/scheduler/airflow2/tests && pip3 install -r requirements.txt && python3 -m unittest discover . generate-proto: ## regenerate protos - @echo " > generating protobuf from odpf/proton" + @echo " > generating protobuf from raystack/proton" @echo " > [info] make sure correct version of dependencies are installed using 'make install'" - @buf generate https://github.com/odpf/proton/archive/${PROTON_COMMIT}.zip#strip_components=1 --template buf.gen.yaml --path odpf/optimus + @buf generate https://github.com/raystack/proton/archive/${PROTON_COMMIT}.zip#strip_components=1 --template buf.gen.yaml --path raystack/optimus @echo " > protobuf compilation finished" unit-test-ci: @@ -53,9 +53,6 @@ bench: coverage: ## print code coverage go test -race -coverprofile coverage.txt -covermode=atomic ./... -tags=unit_test && go tool cover -html=coverage.txt -clean: - rm -rf ./optimus ./dist ./api/proto/* ./api/third_party/odpf/* - lint: golangci-lint run --fix diff --git a/README.md b/README.md index d322fccc58..21e61e1c2c 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # Optimus -[![verify workflow](https://github.com/odpf/optimus/actions/workflows/verify.yml/badge.svg)](verification) -[![publish latest workflow](https://github.com/odpf/optimus/actions/workflows/publish-latest.yml/badge.svg)](build) -[![Coverage Status](https://coveralls.io/repos/github/odpf/optimus/badge.svg?branch=main)](https://coveralls.io/github/odpf/optimus?branch=main) +[![verify workflow](https://github.com/raystack/optimus/actions/workflows/verify.yml/badge.svg)](verification) +[![publish latest workflow](https://github.com/raystack/optimus/actions/workflows/publish-latest.yml/badge.svg)](build) +[![Coverage Status](https://coveralls.io/repos/github/raystack/optimus/badge.svg?branch=main)](https://coveralls.io/github/raystack/optimus?branch=main) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg?logo=apache)](LICENSE) -[![Version](https://img.shields.io/github/v/release/odpf/optimus?logo=semantic-release)](Version) +[![Version](https://img.shields.io/github/v/release/raystack/optimus?logo=semantic-release)](Version) Optimus is an easy-to-use, reliable, and performant workflow orchestrator for data transformation, data modeling, pipelines, and data quality management. It enables data analysts and engineers to transform their data by writing simple SQL queries and YAML configuration while Optimus handles dependency management, scheduling and all other aspects of running transformation jobs at scale. @@ -29,7 +29,7 @@ Discover why users choose Optimus as their main data transformation tool. Optimus has two components, Optimus service that is the core orchestrator installed on server side, and a CLI binary used to interact with this service. You can install Optimus CLI using homebrew on macOS: ```shell -$ brew install odpf/tap/optimus +$ brew install raystack/tap/optimus $ optimus --help Optimus is an easy-to-use, reliable, and performant workflow orchestrator for @@ -67,10 +67,10 @@ Use "optimus [command] --help" for more information about a command. Explore the following resources to get started with Optimus: -- [Guides](https://odpf.github.io/optimus/docs/guides/create-job/) provides guidance on using Optimus. -- [Concepts](https://odpf.github.io/optimus/docs/concepts/overview/) describes all important Optimus concepts. -- [Reference](https://odpf.github.io/optimus/docs/reference/api/) contains details about configurations, metrics and other aspects of Optimus. -- [Contribute](https://odpf.github.io/optimus/docs/contribute/contributing/) contains resources for anyone who wants to contribute to Optimus. +- [Guides](https://raystack.github.io/optimus/docs/guides/create-job/) provides guidance on using Optimus. +- [Concepts](https://raystack.github.io/optimus/docs/concepts/overview/) describes all important Optimus concepts. +- [Reference](https://raystack.github.io/optimus/docs/reference/api/) contains details about configurations, metrics and other aspects of Optimus. +- [Contribute](https://raystack.github.io/optimus/docs/contribute/contributing/) contains resources for anyone who wants to contribute to Optimus. ## Running locally @@ -82,7 +82,7 @@ Optimus requires the following dependencies: Run the following commands to compile `optimus` from source ```shell -$ git clone git@github.com:odpf/optimus.git +$ git clone git@github.com:raystack/optimus.git $ cd optimus $ make ``` @@ -99,7 +99,7 @@ Optimus service can be started with $ ./optimus serve ``` -`serve` command has few required configurations that needs to be set for it to start. Read more about it in [getting started](https://odpf.github.io/optimus/docs/getting-started/configuration). +`serve` command has few required configurations that needs to be set for it to start. Read more about it in [getting started](https://raystack.github.io/optimus/docs/getting-started/configuration). ## Compatibility @@ -109,9 +109,9 @@ Optimus is currently undergoing heavy development with frequent, breaking API ch Development of Optimus happens in the open on GitHub, and we are grateful to the community for contributing bugfixes and improvements. Read below to learn how you can take part in improving Optimus. -Read our [contributing guide](https://odpf.github.io/optimus/docs/contribute/contributing) to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes to Optimus. +Read our [contributing guide](https://raystack.github.io/optimus/docs/contribute/contributing) to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes to Optimus. -To help you get your feet wet and get you familiar with our contribution process, we have a list of [good first issues](https://github.com/odpf/optimus/labels/good%20first%20issue) that contain bugs which have a relatively limited scope. This is a great place to get started. +To help you get your feet wet and get you familiar with our contribution process, we have a list of [good first issues](https://github.com/raystack/optimus/labels/good%20first%20issue) that contain bugs which have a relatively limited scope. This is a great place to get started. ## License diff --git a/buf.gen.yaml b/buf.gen.yaml index a46d1c1099..dc908e7349 100644 --- a/buf.gen.yaml +++ b/buf.gen.yaml @@ -7,7 +7,7 @@ plugins: # proto file should be. # This is necessary while importing a proto file foo/a.proto from another # directory, e.g. bar/b.proto - opt: paths=source_relative,Modpf/optimus/core/v1beta1/job_run.proto=github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1,Modpf/optimus/core/v1beta1/project.proto=github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1,Modpf/optimus/core/v1beta1/namespace.proto=github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1,Modpf/optimus/core/v1beta1/job_spec.proto=github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1,Modpf/optimus/core/v1beta1/scheduler.proto=github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1,Modpf/optimus/core/v1beta1/status.proto=github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1,Modpf/optimus/core/v1beta1/resource.proto=github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1 + opt: paths=source_relative,Mraystack/optimus/core/v1beta1/job_run.proto=github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1,Mraystack/optimus/core/v1beta1/project.proto=github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1,Mraystack/optimus/core/v1beta1/namespace.proto=github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1,Mraystack/optimus/core/v1beta1/job_spec.proto=github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1,Mraystack/optimus/core/v1beta1/scheduler.proto=github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1,Mraystack/optimus/core/v1beta1/status.proto=github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1,Mraystack/optimus/core/v1beta1/resource.proto=github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1 - name: go-grpc out: protos opt: paths=source_relative,require_unimplemented_servers=true @@ -15,4 +15,4 @@ plugins: out: protos opt: paths=source_relative - name: openapiv2 - out: protos \ No newline at end of file + out: protos diff --git a/client/cmd/backup/create.go b/client/cmd/backup/create.go index 86f3e80809..0b9f739411 100644 --- a/client/cmd/backup/create.go +++ b/client/cmd/backup/create.go @@ -7,20 +7,22 @@ import ( "fmt" "strings" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/cmd/internal" - "github.com/odpf/optimus/client/cmd/internal/connectivity" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/client/cmd/internal/progressbar" - "github.com/odpf/optimus/client/cmd/internal/survey" - "github.com/odpf/optimus/config" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/client/cmd/internal" + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/client/cmd/internal/progressbar" + "github.com/raystack/optimus/client/cmd/internal/survey" + "github.com/raystack/optimus/config" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) type createCommand struct { - logger log.Logger + logger log.Logger + connection connection.Connection + configFilePath string isConfigExist bool @@ -128,6 +130,7 @@ func (c *createCommand) fillAttributes(conf *config.ClientConfig) error { } } } + c.connection = connection.New(c.logger, conf) return nil } @@ -150,13 +153,13 @@ func (c *createCommand) RunE(_ *cobra.Command, _ []string) error { } func (c *createCommand) runBackupRequest() error { - conn, err := connectivity.NewConnectivity(c.host, backupTimeout) + conn, err := c.connection.Create(c.host) if err != nil { return err } defer conn.Close() - backup := pb.NewBackupServiceClient(conn.GetConnection()) + backup := pb.NewBackupServiceClient(conn) spinner := progressbar.NewProgressBar() spinner.Start("please wait...") @@ -169,7 +172,11 @@ func (c *createCommand) runBackupRequest() error { Description: c.description, Config: c.dsBackupConfigUnmarshaled, } - backupResponse, err := backup.CreateBackup(conn.GetContext(), backupRequest) + + ctx, dialCancel := context.WithTimeout(context.Background(), backupTimeout) + defer dialCancel() + + backupResponse, err := backup.CreateBackup(ctx, backupRequest) spinner.Stop() if err != nil { diff --git a/client/cmd/backup/list.go b/client/cmd/backup/list.go index 3974cf0866..5c55cba65f 100644 --- a/client/cmd/backup/list.go +++ b/client/cmd/backup/list.go @@ -8,17 +8,17 @@ import ( "strings" "time" - "github.com/odpf/salt/log" "github.com/olekukonko/tablewriter" + "github.com/raystack/salt/log" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/cmd/internal" - "github.com/odpf/optimus/client/cmd/internal/connectivity" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/client/cmd/internal/progressbar" - "github.com/odpf/optimus/client/cmd/internal/survey" - "github.com/odpf/optimus/config" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/client/cmd/internal" + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/client/cmd/internal/progressbar" + "github.com/raystack/optimus/client/cmd/internal/survey" + "github.com/raystack/optimus/config" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) const ( @@ -26,7 +26,9 @@ const ( ) type listCommand struct { - logger log.Logger + logger log.Logger + connection connection.Connection + configFilePath string namespaceSurvey *survey.NamespaceSurvey @@ -93,6 +95,8 @@ func (l *listCommand) PreRunE(cmd *cobra.Command, _ []string) error { l.namespaceName = namespace.Name } + l.connection = connection.New(l.logger, conf) + return nil } @@ -103,7 +107,7 @@ func (l *listCommand) RunE(_ *cobra.Command, _ []string) error { NamespaceName: l.namespaceName, } - conn, err := connectivity.NewConnectivity(l.host, backupTimeout) + conn, err := l.connection.Create(l.host) if err != nil { return err } @@ -111,8 +115,12 @@ func (l *listCommand) RunE(_ *cobra.Command, _ []string) error { spinner := progressbar.NewProgressBar() spinner.Start("please wait...") - backup := pb.NewBackupServiceClient(conn.GetConnection()) - listBackupsResponse, err := backup.ListBackups(conn.GetContext(), listBackupsRequest) + backup := pb.NewBackupServiceClient(conn) + + ctx, dialCancel := context.WithTimeout(context.Background(), backupTimeout) + defer dialCancel() + + listBackupsResponse, err := backup.ListBackups(ctx, listBackupsRequest) spinner.Stop() if err != nil { if errors.Is(err, context.DeadlineExceeded) { diff --git a/client/cmd/backup/status.go b/client/cmd/backup/status.go index 941515d88f..8b6e42a560 100644 --- a/client/cmd/backup/status.go +++ b/client/cmd/backup/status.go @@ -8,20 +8,22 @@ import ( "strings" "time" - "github.com/odpf/salt/log" "github.com/olekukonko/tablewriter" + "github.com/raystack/salt/log" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/cmd/internal" - "github.com/odpf/optimus/client/cmd/internal/connectivity" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/client/cmd/internal/progressbar" - "github.com/odpf/optimus/config" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/client/cmd/internal" + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/client/cmd/internal/progressbar" + "github.com/raystack/optimus/config" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) type statusCommand struct { - logger log.Logger + logger log.Logger + connection connection.Connection + configFilePath string projectName string @@ -76,6 +78,8 @@ func (s *statusCommand) PreRunE(cmd *cobra.Command, _ []string) error { if s.host == "" { s.host = conf.Host } + + s.connection = connection.New(s.logger, conf) return nil } @@ -86,17 +90,21 @@ func (s *statusCommand) RunE(_ *cobra.Command, args []string) error { Id: args[0], } - conn, err := connectivity.NewConnectivity(s.host, backupTimeout) + conn, err := s.connection.Create(s.host) if err != nil { return err } defer conn.Close() - backup := pb.NewBackupServiceClient(conn.GetConnection()) + backup := pb.NewBackupServiceClient(conn) spinner := progressbar.NewProgressBar() spinner.Start("please wait...") - backupDetailResponse, err := backup.GetBackup(conn.GetContext(), getBackupRequest) + + ctx, dialCancel := context.WithTimeout(context.Background(), backupTimeout) + defer dialCancel() + + backupDetailResponse, err := backup.GetBackup(ctx, getBackupRequest) spinner.Stop() if err != nil { if errors.Is(err, context.DeadlineExceeded) { diff --git a/client/cmd/commands.go b/client/cmd/commands.go index 23c4e8fea0..9530775199 100644 --- a/client/cmd/commands.go +++ b/client/cmd/commands.go @@ -2,22 +2,22 @@ package cmd import ( "github.com/MakeNowJust/heredoc" - "github.com/odpf/salt/cmdx" + "github.com/raystack/salt/cmdx" cli "github.com/spf13/cobra" - "github.com/odpf/optimus/client/cmd/backup" - "github.com/odpf/optimus/client/cmd/extension" - "github.com/odpf/optimus/client/cmd/initialize" - "github.com/odpf/optimus/client/cmd/job" - "github.com/odpf/optimus/client/cmd/namespace" - "github.com/odpf/optimus/client/cmd/playground" - "github.com/odpf/optimus/client/cmd/plugin" - "github.com/odpf/optimus/client/cmd/project" - "github.com/odpf/optimus/client/cmd/replay" - "github.com/odpf/optimus/client/cmd/resource" - "github.com/odpf/optimus/client/cmd/scheduler" - "github.com/odpf/optimus/client/cmd/secret" - "github.com/odpf/optimus/client/cmd/version" + "github.com/raystack/optimus/client/cmd/backup" + "github.com/raystack/optimus/client/cmd/extension" + "github.com/raystack/optimus/client/cmd/initialize" + "github.com/raystack/optimus/client/cmd/job" + "github.com/raystack/optimus/client/cmd/namespace" + "github.com/raystack/optimus/client/cmd/playground" + "github.com/raystack/optimus/client/cmd/plugin" + "github.com/raystack/optimus/client/cmd/project" + "github.com/raystack/optimus/client/cmd/replay" + "github.com/raystack/optimus/client/cmd/resource" + "github.com/raystack/optimus/client/cmd/scheduler" + "github.com/raystack/optimus/client/cmd/secret" + "github.com/raystack/optimus/client/cmd/version" ) // New constructs the 'root' command. It houses all other sub commands @@ -45,10 +45,10 @@ func New() *cli.Command { "group:core": "true", "help:learn": heredoc.Doc(` Use 'optimus --help' for more information about a command. - Read the manual at https://odpf.github.io/optimus/ + Read the manual at https://raystack.github.io/optimus/ `), "help:feedback": heredoc.Doc(` - Open an issue here https://github.com/odpf/optimus/issues + Open an issue here https://github.com/raystack/optimus/issues `), }, } diff --git a/client/cmd/extension/activate.go b/client/cmd/extension/activate.go index 8a4adecfb0..0ef827bc51 100644 --- a/client/cmd/extension/activate.go +++ b/client/cmd/extension/activate.go @@ -3,10 +3,10 @@ package extension import ( "errors" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/model" ) type activateCommand struct { diff --git a/client/cmd/extension/clean.go b/client/cmd/extension/clean.go index aa35043116..fcccf90e38 100644 --- a/client/cmd/extension/clean.go +++ b/client/cmd/extension/clean.go @@ -1,11 +1,11 @@ package extension import ( - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/cmd/internal/survey" - "github.com/odpf/optimus/client/extension" + "github.com/raystack/optimus/client/cmd/internal/survey" + "github.com/raystack/optimus/client/extension" ) type cleanCommand struct { diff --git a/client/cmd/extension/describe.go b/client/cmd/extension/describe.go index 05e159c226..93b7621c2e 100644 --- a/client/cmd/extension/describe.go +++ b/client/cmd/extension/describe.go @@ -4,11 +4,11 @@ import ( "bytes" "fmt" - "github.com/odpf/salt/log" "github.com/olekukonko/tablewriter" + "github.com/raystack/salt/log" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/model" ) type describeCommand struct { diff --git a/client/cmd/extension/extension.go b/client/cmd/extension/extension.go index fe7961b419..191aabd796 100644 --- a/client/cmd/extension/extension.go +++ b/client/cmd/extension/extension.go @@ -4,12 +4,12 @@ import ( "fmt" "os" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/client/extension" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/client/extension" + "github.com/raystack/optimus/client/extension/model" ) // UpdateWithExtension updates input command with the available extensions diff --git a/client/cmd/extension/install.go b/client/cmd/extension/install.go index f44096e955..9c3ce594dc 100644 --- a/client/cmd/extension/install.go +++ b/client/cmd/extension/install.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/cobra" ) diff --git a/client/cmd/extension/rename.go b/client/cmd/extension/rename.go index 7ff5342f81..4b58c5b4d0 100644 --- a/client/cmd/extension/rename.go +++ b/client/cmd/extension/rename.go @@ -3,10 +3,10 @@ package extension import ( "errors" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/model" ) type renameCommand struct { diff --git a/client/cmd/extension/uninstall.go b/client/cmd/extension/uninstall.go index f11df1142e..c89577a8f8 100644 --- a/client/cmd/extension/uninstall.go +++ b/client/cmd/extension/uninstall.go @@ -1,11 +1,11 @@ package extension import ( - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/cmd/internal/survey" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/cmd/internal/survey" + "github.com/raystack/optimus/client/extension/model" ) type uninstallCommand struct { diff --git a/client/cmd/extension/upgrade.go b/client/cmd/extension/upgrade.go index 55a8f6dccc..88bfc0cca5 100644 --- a/client/cmd/extension/upgrade.go +++ b/client/cmd/extension/upgrade.go @@ -3,10 +3,10 @@ package extension import ( "context" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/model" ) type upgradeCommand struct { diff --git a/client/cmd/initialize/initialize.go b/client/cmd/initialize/initialize.go index 91c8b2cf1b..fe06db6e26 100644 --- a/client/cmd/initialize/initialize.go +++ b/client/cmd/initialize/initialize.go @@ -6,14 +6,14 @@ import ( "os" "path" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/cobra" "gopkg.in/yaml.v2" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/client/cmd/internal/survey" - "github.com/odpf/optimus/config" - "github.com/odpf/optimus/internal/utils" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/client/cmd/internal/survey" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/internal/utils" ) type initializeCommand struct { diff --git a/client/cmd/internal/auth/auth.go b/client/cmd/internal/auth/auth.go new file mode 100644 index 0000000000..6a8406807b --- /dev/null +++ b/client/cmd/internal/auth/auth.go @@ -0,0 +1,59 @@ +package auth + +import ( + "context" + + "github.com/raystack/salt/log" + "github.com/raystack/salt/oidc" + "golang.org/x/oauth2" + "golang.org/x/oauth2/google" + + "github.com/raystack/optimus/config" +) + +type Auth struct { + logger log.Logger + cfg *oauth2.Config +} + +func NewAuth(logger log.Logger, authConfig config.Auth) *Auth { + return &Auth{ + logger: logger, + cfg: toAuthConfig(authConfig), + } +} + +func (a Auth) GetToken(ctx context.Context) (*oauth2.Token, error) { + token, err := RetrieveFromKeyring(a.cfg.ClientID) + if err == nil { + return token, nil + } + + token, err = a.getTokenFromServer(ctx, a.cfg) + if err == nil { + err = StoreInKeyring(a.cfg.ClientID, token) + if err != nil { + a.logger.Debug("not able to save token in keyring") + } + return token, nil + } + return nil, err +} + +func (Auth) getTokenFromServer(ctx context.Context, cfg *oauth2.Config) (*oauth2.Token, error) { + source := oidc.NewTokenSource(ctx, cfg, cfg.ClientID) + return source.Token() +} + +func toAuthConfig(authConfig config.Auth) *oauth2.Config { + callbackURL := "http://localhost:9090/auth/callback" + cfg := &oauth2.Config{ + ClientID: authConfig.ClientID, + ClientSecret: authConfig.ClientSecret, + Endpoint: google.Endpoint, + RedirectURL: callbackURL, + Scopes: []string{"openid email"}, + } + + return cfg +} diff --git a/client/cmd/internal/auth/keyring.go b/client/cmd/internal/auth/keyring.go new file mode 100644 index 0000000000..298fea3f9f --- /dev/null +++ b/client/cmd/internal/auth/keyring.go @@ -0,0 +1,41 @@ +package auth + +import ( + "encoding/json" + "errors" + + "github.com/zalando/go-keyring" + "golang.org/x/oauth2" +) + +const ( + keyringService = "optimus" +) + +func RetrieveFromKeyring(clientID string) (*oauth2.Token, error) { + tokenStr, err := keyring.Get(keyringService, clientID) + if err != nil { + return nil, err + } + + var token oauth2.Token + if err := json.Unmarshal([]byte(tokenStr), &token); err != nil { + return nil, err + } + + if !token.Valid() { + return nil, errors.New("token is not valid") + } + + return &token, err +} + +func StoreInKeyring(clientID string, t *oauth2.Token) error { + tokenBytes, err := json.Marshal(t) + if err != nil { + return err + } + + tokenStr := string(tokenBytes) + return keyring.Set(keyringService, clientID, tokenStr) +} diff --git a/client/cmd/internal/connection/connection.go b/client/cmd/internal/connection/connection.go new file mode 100644 index 0000000000..545f688eed --- /dev/null +++ b/client/cmd/internal/connection/connection.go @@ -0,0 +1,77 @@ +package connection + +import ( + "errors" + "os" + "time" + + "github.com/MakeNowJust/heredoc" + grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" + grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry" + grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" + "github.com/raystack/salt/log" + "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" + "google.golang.org/grpc" + + "github.com/raystack/optimus/config" +) + +const ( + grpcMaxClientSendSize = 128 << 20 // 128MB + grpcMaxClientRecvSize = 128 << 20 // 128MB + grpcMaxRetry uint = 3 + + optimusDialTimeout = time.Second * 2 + backoffDuration = 100 * time.Millisecond +) + +var errServerNotReachable = func(host string) error { + return errors.New(heredoc.Docf(`Unable to reach optimus server at %s, this can happen due to following reasons: + 1. Check if you are connected to internet + 2. Is the host correctly configured in optimus config + 3. Is Optimus server currently unreachable`, host)) +} + +type Connection interface { + Create(host string) (*grpc.ClientConn, error) +} + +func New(l log.Logger, cfg *config.ClientConfig) Connection { + if useInsecure() { + return NewInsecure(l) + } + + return NewSecure(l, cfg) +} + +func useInsecure() bool { + if insecure := os.Getenv("OPTIMUS_INSECURE"); insecure != "" { + return true + } + return false +} + +func defaultDialOptions() []grpc.DialOption { + retryOpts := []grpc_retry.CallOption{ + grpc_retry.WithBackoff(grpc_retry.BackoffExponential(backoffDuration)), + grpc_retry.WithMax(grpcMaxRetry), + } + var opts []grpc.DialOption + opts = append(opts, + grpc.WithBlock(), + grpc.WithDefaultCallOptions( + grpc.MaxCallSendMsgSize(grpcMaxClientSendSize), + grpc.MaxCallRecvMsgSize(grpcMaxClientRecvSize), + ), + grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient( + grpc_retry.UnaryClientInterceptor(retryOpts...), + otelgrpc.UnaryClientInterceptor(), + grpc_prometheus.UnaryClientInterceptor, + )), + grpc.WithStreamInterceptor(grpc_middleware.ChainStreamClient( + otelgrpc.StreamClientInterceptor(), + grpc_prometheus.StreamClientInterceptor, + )), + ) + return opts +} diff --git a/client/cmd/internal/connection/insecure.go b/client/cmd/internal/connection/insecure.go new file mode 100644 index 0000000000..1aad043ae6 --- /dev/null +++ b/client/cmd/internal/connection/insecure.go @@ -0,0 +1,34 @@ +package connection + +import ( + "context" + "errors" + + "github.com/raystack/salt/log" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +type Insecure struct { + l log.Logger +} + +func NewInsecure(l log.Logger) *Insecure { + return &Insecure{ + l: l, + } +} + +func (*Insecure) Create(host string) (*grpc.ClientConn, error) { + ctx, dialCancel := context.WithTimeout(context.Background(), optimusDialTimeout) + defer dialCancel() + + opts := append(defaultDialOptions(), grpc.WithTransportCredentials(insecure.NewCredentials())) + + conn, err := grpc.DialContext(ctx, host, opts...) + if errors.Is(err, context.DeadlineExceeded) { + err = errServerNotReachable(host) + } + + return conn, err +} diff --git a/client/cmd/internal/connection/secure.go b/client/cmd/internal/connection/secure.go new file mode 100644 index 0000000000..db32541eaa --- /dev/null +++ b/client/cmd/internal/connection/secure.go @@ -0,0 +1,95 @@ +package connection + +import ( + "context" + "crypto/tls" + "crypto/x509" + "errors" + "fmt" + "time" + + "github.com/raystack/salt/log" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + + "github.com/raystack/optimus/client/cmd/internal/auth" + "github.com/raystack/optimus/config" +) + +const authTimeout = time.Minute * 1 + +type Secure struct { + l log.Logger + authConfig config.Auth +} + +func NewSecure(l log.Logger, cfg *config.ClientConfig) *Secure { + return &Secure{ + l: l, + authConfig: cfg.Auth, + } +} + +func (s *Secure) Create(host string) (*grpc.ClientConn, error) { + ctx, dialCancel := context.WithTimeout(context.Background(), optimusDialTimeout) + defer dialCancel() + + opts, err := s.getOptionsWithAuth() + if err != nil { + return nil, err + } + + conn, err := grpc.DialContext(ctx, host, opts...) + if errors.Is(err, context.DeadlineExceeded) { + err = errServerNotReachable(host) + } + + return conn, err +} + +func (s *Secure) getOptionsWithAuth() ([]grpc.DialOption, error) { + if s.authConfig.ClientID == "" || s.authConfig.ClientSecret == "" { + return nil, errors.New("invalid auth configuration, clientID or clientSecret is empty") + } + + // setup https connection + tlsCredentials, err := loadTLSCredentials() + if err != nil { + return nil, err + } + + opts := append(defaultDialOptions(), grpc.WithTransportCredentials(tlsCredentials)) + + // add the token for authentication + a := auth.NewAuth(s.l, s.authConfig) + + ctx, dialCancel := context.WithTimeout(context.Background(), authTimeout) + defer dialCancel() + + token, err := a.GetToken(ctx) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New("unable to get valid token") + } + + opts = append(opts, grpc.WithPerRPCCredentials(&bearerAuthentication{ + Token: token.AccessToken, + })) + + return opts, nil +} + +func loadTLSCredentials() (credentials.TransportCredentials, error) { + certPool, err := x509.SystemCertPool() + if err != nil { + return nil, fmt.Errorf("unable to read system certs") + } + + tlsConfig := &tls.Config{ + RootCAs: certPool, + MinVersion: tls.VersionTLS12, + } + return credentials.NewTLS(tlsConfig), nil +} diff --git a/client/cmd/internal/connectivity/authentication.go b/client/cmd/internal/connection/token_auth.go similarity index 50% rename from client/cmd/internal/connectivity/authentication.go rename to client/cmd/internal/connection/token_auth.go index b03c5c6896..dbd002358a 100644 --- a/client/cmd/internal/connectivity/authentication.go +++ b/client/cmd/internal/connection/token_auth.go @@ -1,4 +1,4 @@ -package connectivity +package connection import ( "context" @@ -18,17 +18,3 @@ func (a *bearerAuthentication) GetRequestMetadata(context.Context, ...string) (m func (*bearerAuthentication) RequireTransportSecurity() bool { return false } - -type basicAuthentication struct { - Token string -} - -func (a *basicAuthentication) GetRequestMetadata(context.Context, ...string) (map[string]string, error) { - return map[string]string{ - "Authorization": fmt.Sprintf("Basic %s", a.Token), - }, nil -} - -func (*basicAuthentication) RequireTransportSecurity() bool { - return false -} diff --git a/client/cmd/internal/connectivity/connectivity.go b/client/cmd/internal/connectivity/connectivity.go deleted file mode 100644 index 6fb5749cdf..0000000000 --- a/client/cmd/internal/connectivity/connectivity.go +++ /dev/null @@ -1,120 +0,0 @@ -package connectivity - -import ( - "context" - "encoding/base64" - "errors" - "os" - "time" - - "github.com/MakeNowJust/heredoc" - grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" - grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry" - grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" - "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" - "google.golang.org/grpc" -) - -const ( - grpcMaxClientSendSize = 128 << 20 // 128MB - grpcMaxClientRecvSize = 128 << 20 // 128MB - grpcMaxRetry uint = 3 - - optimusDialTimeout = time.Second * 2 - backoffDuration = 100 * time.Millisecond -) - -var errServerNotReachable = func(host string) error { - return errors.New(heredoc.Docf(`Unable to reach optimus server at %s, this can happen due to following reasons: - 1. Check if you are connected to internet - 2. Is the host correctly configured in optimus config - 3. Is Optimus server currently unreachable`, host)) -} - -// Connectivity defines client connection to a targeted server host -type Connectivity struct { - requestCtx context.Context //nolint:containedctx - cancelRequestCtx func() - - connection *grpc.ClientConn -} - -// NewConnectivity initializes client connection -func NewConnectivity(serverHost string, requestTimeout time.Duration) (*Connectivity, error) { - connection, err := createConnection(serverHost) - if err != nil { - return nil, err - } - reqCtx, reqCancel := context.WithTimeout(context.Background(), requestTimeout) - return &Connectivity{ - requestCtx: reqCtx, - cancelRequestCtx: reqCancel, - connection: connection, - }, nil -} - -// GetContext gets request context -func (c *Connectivity) GetContext() context.Context { - return c.requestCtx -} - -// GetConnection gets client connection -func (c *Connectivity) GetConnection() *grpc.ClientConn { - return c.connection -} - -// Close closes client connection and its context -func (c *Connectivity) Close() { - c.connection.Close() - c.cancelRequestCtx() -} - -func createConnection(host string) (*grpc.ClientConn, error) { - opts := getDefaultDialOptions() - - // pass rpc credentials - if token := os.Getenv("OPTIMUS_AUTH_BASIC_TOKEN"); token != "" { - base64Token := base64.StdEncoding.EncodeToString([]byte(token)) - opts = append(opts, grpc.WithPerRPCCredentials(&basicAuthentication{ - Token: base64Token, - })) - } else if token := os.Getenv("OPTIMUS_AUTH_BEARER_TOKEN"); token != "" { - opts = append(opts, grpc.WithPerRPCCredentials(&bearerAuthentication{ - Token: token, - })) - } - - ctx, dialCancel := context.WithTimeout(context.Background(), optimusDialTimeout) - conn, err := grpc.DialContext(ctx, host, opts...) - if errors.Is(err, context.DeadlineExceeded) { - err = errServerNotReachable(host) - } - dialCancel() - return conn, err -} - -func getDefaultDialOptions() []grpc.DialOption { - retryOpts := []grpc_retry.CallOption{ - grpc_retry.WithBackoff(grpc_retry.BackoffExponential(backoffDuration)), - grpc_retry.WithMax(grpcMaxRetry), - } - var opts []grpc.DialOption - opts = append(opts, - grpc.WithInsecure(), - grpc.WithBlock(), - grpc.WithDefaultCallOptions( - grpc.MaxCallSendMsgSize(grpcMaxClientSendSize), - grpc.MaxCallRecvMsgSize(grpcMaxClientRecvSize), - ), - grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient( - grpc_retry.UnaryClientInterceptor(retryOpts...), - otelgrpc.UnaryClientInterceptor(), - grpc_prometheus.UnaryClientInterceptor, - )), - grpc.WithStreamInterceptor(grpc_middleware.ChainStreamClient( - otelgrpc.StreamClientInterceptor(), - grpc_prometheus.StreamClientInterceptor, - )), - ) - return opts -} diff --git a/client/cmd/internal/load_client_config.go b/client/cmd/internal/load_client_config.go index 82d42ef8cc..bed44e323b 100644 --- a/client/cmd/internal/load_client_config.go +++ b/client/cmd/internal/load_client_config.go @@ -3,9 +3,9 @@ package internal import ( "errors" - saltConfig "github.com/odpf/salt/config" + saltConfig "github.com/raystack/salt/config" - "github.com/odpf/optimus/config" + "github.com/raystack/optimus/config" ) // TODO: need to do refactor for proper file naming diff --git a/client/cmd/internal/logger/logger.go b/client/cmd/internal/logger/logger.go index e1c721be20..d3718d738a 100644 --- a/client/cmd/internal/logger/logger.go +++ b/client/cmd/internal/logger/logger.go @@ -6,7 +6,7 @@ import ( "os" "github.com/fatih/color" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" ) type defaultLogger struct { diff --git a/client/cmd/internal/logger/printer.go b/client/cmd/internal/logger/printer.go index 9f4f40f975..276e292545 100644 --- a/client/cmd/internal/logger/printer.go +++ b/client/cmd/internal/logger/printer.go @@ -1,9 +1,9 @@ package logger import ( - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) func PrintLogStatusVerbose(logger log.Logger, logStatus *pb.Log) { diff --git a/client/cmd/internal/manage_plugins.go b/client/cmd/internal/manage_plugins.go index 2af1f817c9..2d7107ac17 100644 --- a/client/cmd/internal/manage_plugins.go +++ b/client/cmd/internal/manage_plugins.go @@ -6,9 +6,9 @@ import ( "github.com/hashicorp/go-hclog" hPlugin "github.com/hashicorp/go-plugin" - "github.com/odpf/optimus/config" - "github.com/odpf/optimus/internal/models" - oPlugin "github.com/odpf/optimus/plugin" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/internal/models" + oPlugin "github.com/raystack/optimus/plugin" ) // InitPlugins triggers initialization of all available plugins diff --git a/client/cmd/internal/progressbar/progress_bar.go b/client/cmd/internal/progressbar/progress_bar.go index faeb731ca7..a778240704 100644 --- a/client/cmd/internal/progressbar/progress_bar.go +++ b/client/cmd/internal/progressbar/progress_bar.go @@ -1,6 +1,7 @@ package progressbar import ( + "fmt" "io" "os" "strings" @@ -10,7 +11,7 @@ import ( "github.com/briandowns/spinner" "github.com/schollz/progressbar/v3" - "github.com/odpf/optimus/internal/utils" + "github.com/raystack/optimus/internal/utils" ) const ( @@ -67,6 +68,14 @@ func (p *ProgressBar) Start(label string) { p.spinner = sp } +func (p *ProgressBar) StartNewLine(label string) { + p.spinner.FinalMSG = fmt.Sprintf("✓%s", p.spinner.Suffix) + p.Stop() + p.writer.Write([]byte("\n")) + p.spinner = nil + p.Start(label) +} + // StartProgress starts progress bar with count and label func (p *ProgressBar) StartProgress(count int, label string) { p.mu.Lock() diff --git a/client/cmd/internal/survey/backup_create.go b/client/cmd/internal/survey/backup_create.go index 6aba151aa7..aa7b066a1f 100644 --- a/client/cmd/internal/survey/backup_create.go +++ b/client/cmd/internal/survey/backup_create.go @@ -2,7 +2,7 @@ package survey import ( "github.com/AlecAivazis/survey/v2" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" ) // BackupCreateSurvey defines survey for creating backup diff --git a/client/cmd/internal/survey/initialize.go b/client/cmd/internal/survey/initialize.go index 24eb48fb10..74ea5ce501 100644 --- a/client/cmd/internal/survey/initialize.go +++ b/client/cmd/internal/survey/initialize.go @@ -5,10 +5,10 @@ import ( "path" "github.com/AlecAivazis/survey/v2" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" - "github.com/odpf/optimus/config" - "github.com/odpf/optimus/internal/utils" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/internal/utils" ) // InititalizeSurvey defines surveys related to init client config diff --git a/client/cmd/internal/survey/job.go b/client/cmd/internal/survey/job.go index 9b9dd3a129..a7ea167cae 100644 --- a/client/cmd/internal/survey/job.go +++ b/client/cmd/internal/survey/job.go @@ -9,9 +9,9 @@ import ( "github.com/AlecAivazis/survey/v2" - "github.com/odpf/optimus/client/local" - "github.com/odpf/optimus/client/local/model" - "github.com/odpf/optimus/sdk/plugin" + "github.com/raystack/optimus/client/local" + "github.com/raystack/optimus/client/local/model" + "github.com/raystack/optimus/sdk/plugin" ) // JobSurvey defines survey for job specification in general diff --git a/client/cmd/internal/survey/job_addhook.go b/client/cmd/internal/survey/job_addhook.go index b1db35af84..29fc096181 100644 --- a/client/cmd/internal/survey/job_addhook.go +++ b/client/cmd/internal/survey/job_addhook.go @@ -7,9 +7,9 @@ import ( "github.com/AlecAivazis/survey/v2" - "github.com/odpf/optimus/client/local/model" - "github.com/odpf/optimus/internal/models" - "github.com/odpf/optimus/sdk/plugin" + "github.com/raystack/optimus/client/local/model" + "github.com/raystack/optimus/internal/models" + "github.com/raystack/optimus/sdk/plugin" ) // JobAddHookSurvey defines survey for job add hook diff --git a/client/cmd/internal/survey/job_create.go b/client/cmd/internal/survey/job_create.go index 5cf872379a..7907bb08f1 100644 --- a/client/cmd/internal/survey/job_create.go +++ b/client/cmd/internal/survey/job_create.go @@ -9,11 +9,11 @@ import ( "github.com/AlecAivazis/survey/v2" - "github.com/odpf/optimus/client/local" - "github.com/odpf/optimus/client/local/model" - "github.com/odpf/optimus/internal/models" - "github.com/odpf/optimus/internal/utils" - "github.com/odpf/optimus/sdk/plugin" + "github.com/raystack/optimus/client/local" + "github.com/raystack/optimus/client/local/model" + "github.com/raystack/optimus/internal/models" + "github.com/raystack/optimus/internal/utils" + "github.com/raystack/optimus/sdk/plugin" ) const ( @@ -210,7 +210,6 @@ func (j *JobCreateSurvey) askCreateQuestions(questions []*survey.Question) (mode }, Asset: map[string]string{}, Behavior: model.JobSpecBehavior{ - Catchup: false, DependsOnPast: false, }, Dependencies: []model.JobSpecDependency{}, diff --git a/client/cmd/internal/survey/namespace.go b/client/cmd/internal/survey/namespace.go index 71b892ab84..516cb533ca 100644 --- a/client/cmd/internal/survey/namespace.go +++ b/client/cmd/internal/survey/namespace.go @@ -4,9 +4,9 @@ import ( "errors" "github.com/AlecAivazis/survey/v2" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" - "github.com/odpf/optimus/config" + "github.com/raystack/optimus/config" ) // NamespaceSurvey defines surveys related to namespace diff --git a/client/cmd/internal/survey/resource_create.go b/client/cmd/internal/survey/resource_create.go index 6030081ee8..2232474a7a 100644 --- a/client/cmd/internal/survey/resource_create.go +++ b/client/cmd/internal/survey/resource_create.go @@ -7,8 +7,8 @@ import ( "github.com/AlecAivazis/survey/v2" - "github.com/odpf/optimus/client/local" - "github.com/odpf/optimus/client/local/model" + "github.com/raystack/optimus/client/local" + "github.com/raystack/optimus/client/local/model" ) // ResourceSpecCreateSurvey defines surveys for resource spec creation diff --git a/client/cmd/internal/survey/survey.go b/client/cmd/internal/survey/survey.go index fe2e621b99..6583eacb0c 100644 --- a/client/cmd/internal/survey/survey.go +++ b/client/cmd/internal/survey/survey.go @@ -8,7 +8,7 @@ import ( petname "github.com/dustinkirkland/golang-petname" "github.com/spf13/afero" - "github.com/odpf/optimus/internal/utils" + "github.com/raystack/optimus/internal/utils" ) const ( diff --git a/client/cmd/job/addhook.go b/client/cmd/job/addhook.go index a932c50785..a0c4169c04 100644 --- a/client/cmd/job/addhook.go +++ b/client/cmd/job/addhook.go @@ -1,16 +1,16 @@ package job import ( - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/afero" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/cmd/internal" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/client/cmd/internal/survey" - "github.com/odpf/optimus/client/local/specio" - "github.com/odpf/optimus/config" - "github.com/odpf/optimus/internal/models" + "github.com/raystack/optimus/client/cmd/internal" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/client/cmd/internal/survey" + "github.com/raystack/optimus/client/local/specio" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/internal/models" ) type addHookCommand struct { diff --git a/client/cmd/job/change_namespace.go b/client/cmd/job/change_namespace.go new file mode 100644 index 0000000000..7869ec42cb --- /dev/null +++ b/client/cmd/job/change_namespace.go @@ -0,0 +1,191 @@ +package job + +import ( + "context" + "fmt" + "os" + "path/filepath" + "strings" + "time" + + "github.com/raystack/salt/log" + "github.com/spf13/afero" + "github.com/spf13/cobra" + + "github.com/raystack/optimus/client/cmd/internal" + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/client/local/specio" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" +) + +const ( + changeNamespaceTimeout = time.Minute * 1 +) + +type changeNamespaceCommand struct { + logger log.Logger + connection connection.Connection + + configFilePath string + clientConfig *config.ClientConfig + + project string + oldNamespaceName string + newNamespaceName string + host string +} + +// NewChangeNamespaceCommand initializes job namespace change command +func NewChangeNamespaceCommand() *cobra.Command { + l := logger.NewClientLogger() + changeNamespace := &changeNamespaceCommand{ + logger: l, + } + cmd := &cobra.Command{ + Use: "change-namespace", + Short: "Change namespace of a Job", + Example: "optimus job change-namespace --old-namespace --new-namespace ", + Args: cobra.MinimumNArgs(1), + PreRunE: changeNamespace.PreRunE, + RunE: changeNamespace.RunE, + PostRunE: changeNamespace.PostRunE, + } + // Config filepath flag + cmd.Flags().StringVarP(&changeNamespace.configFilePath, "config", "c", config.EmptyPath, "File path for client configuration") + internal.MarkFlagsRequired(cmd, []string{"old-namespace", "new-namespace"}) + changeNamespace.injectFlags(cmd) + + return cmd +} + +func (c *changeNamespaceCommand) injectFlags(cmd *cobra.Command) { + // Mandatory flags + cmd.Flags().StringVarP(&c.oldNamespaceName, "old-namespace", "o", "", "current namespace of the job") + cmd.Flags().StringVarP(&c.newNamespaceName, "new-namespace", "n", "", "namespace to which the job needs to be moved to") + + // Mandatory flags if config is not set + cmd.Flags().StringVarP(&c.project, "project-name", "p", "", "Name of the optimus project") + cmd.Flags().StringVar(&c.host, "host", "", "Optimus service endpoint url") +} + +func (c *changeNamespaceCommand) PreRunE(_ *cobra.Command, _ []string) error { + // Load mandatory config + conf, err := config.LoadClientConfig(c.configFilePath) + if err != nil { + return err + } + + c.clientConfig = conf + c.connection = connection.New(c.logger, conf) + + return err +} + +func (c *changeNamespaceCommand) RunE(_ *cobra.Command, args []string) error { + jobName := args[0] + err := c.sendChangeNamespaceRequest(jobName) + if err != nil { + return fmt.Errorf("namespace change request failed for job %s: %w", jobName, err) + } + c.logger.Info("[OK] Successfully changed namespace and deployed new DAG on Scheduler") + return nil +} + +func (c *changeNamespaceCommand) sendChangeNamespaceRequest(jobName string) error { + conn, err := c.connection.Create(c.host) + if err != nil { + return err + } + defer conn.Close() + + // fetch Instance by calling the optimus API + jobRunServiceClient := pb.NewJobSpecificationServiceClient(conn) + request := &pb.ChangeJobNamespaceRequest{ + ProjectName: c.project, + NamespaceName: c.oldNamespaceName, + NewNamespaceName: c.newNamespaceName, + JobName: jobName, + } + + ctx, dialCancel := context.WithTimeout(context.Background(), changeNamespaceTimeout) + defer dialCancel() + + _, err = jobRunServiceClient.ChangeJobNamespace(ctx, request) + return err +} + +func (c *changeNamespaceCommand) PostRunE(_ *cobra.Command, args []string) error { + c.logger.Info("\n[info] Moving job in filesystem") + jobName := args[0] + + oldNamespaceConfig, err := c.getNamespaceConfig(c.oldNamespaceName) + if err != nil { + c.logger.Error(fmt.Sprintf("[error] old namespace unregistered in filesystem, err: %s", err.Error())) + return nil + } + + jobSpecReadWriter, err := specio.NewJobSpecReadWriter(afero.NewOsFs()) + if err != nil { + c.logger.Error(fmt.Sprintf("[error] could not instantiate Spec Readed, err: %s", err.Error())) + return nil + } + + jobSpec, err := jobSpecReadWriter.ReadByName(oldNamespaceConfig.Job.Path, jobName) + if err != nil { + c.logger.Error(fmt.Sprintf("[error] unable to find job in old namespace directory, err: %s", err.Error())) + return nil + } + + fs := afero.NewOsFs() + newNamespaceConfig, err := c.getNamespaceConfig(c.newNamespaceName) + if err != nil || newNamespaceConfig.Job.Path == "" { + c.logger.Warn("[warn] new namespace not recognised for jobs") + c.logger.Warn("[info] run `optimus job export` on the new namespace repo, to fetch the newly moved job.") + + c.logger.Warn("[info] removing job from old namespace") + err = fs.RemoveAll(jobSpec.Path) + if err != nil { + c.logger.Error(fmt.Sprintf("[error] unable to remove job from old namespace , err: %s", err.Error())) + c.logger.Warn("[info] consider deleting source files manually if they exist") + return nil + } + c.logger.Warn("[OK] removed job spec from current namespace directory") + return nil + } + + newJobPath := strings.Replace(jobSpec.Path, oldNamespaceConfig.Job.Path, newNamespaceConfig.Job.Path, 1) + + c.logger.Info(fmt.Sprintf("\t* Old Path : '%s' \n\t* New Path : '%s' \n", jobSpec.Path, newJobPath)) + + c.logger.Info(fmt.Sprintf("[info] creating job directry: %s", newJobPath)) + + err = fs.MkdirAll(filepath.Dir(newJobPath), os.ModePerm) + if err != nil { + c.logger.Error(fmt.Sprintf("[error] unable to create path in the new namespace directory, err: %s", err.Error())) + c.logger.Warn("[warn] unable to move job from old namespace") + c.logger.Warn("[info] consider moving source files manually") + return nil + } + + err = fs.Rename(jobSpec.Path, newJobPath) + if err != nil { + c.logger.Error(fmt.Sprintf("[warn] unable to move job from old namespace, err: %s", err.Error())) + c.logger.Warn("[info] consider moving source files manually") + return nil + } + c.logger.Info("[OK] Job moved successfully") + return nil +} + +func (c *changeNamespaceCommand) getNamespaceConfig(namespaceName string) (*config.Namespace, error) { + for _, namespace := range c.clientConfig.Namespaces { + if namespace.Name == namespaceName { + return namespace, nil + } + } + return nil, errors.NotFound(tenant.EntityNamespace, "not recognised in config") +} diff --git a/client/cmd/job/create.go b/client/cmd/job/create.go index 5df163ba90..9db3f82a27 100644 --- a/client/cmd/job/create.go +++ b/client/cmd/job/create.go @@ -5,16 +5,16 @@ import ( "path/filepath" "strings" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/afero" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/cmd/internal" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/client/cmd/internal/survey" - "github.com/odpf/optimus/client/local/specio" - "github.com/odpf/optimus/config" - "github.com/odpf/optimus/internal/models" + "github.com/raystack/optimus/client/cmd/internal" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/client/cmd/internal/survey" + "github.com/raystack/optimus/client/local/specio" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/internal/models" ) type createCommand struct { diff --git a/client/cmd/job/export.go b/client/cmd/job/export.go index 2ea23aaf7b..ead8eb308b 100644 --- a/client/cmd/job/export.go +++ b/client/cmd/job/export.go @@ -1,23 +1,24 @@ package job import ( + "context" "errors" "fmt" "path" "strings" "time" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/afero" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/cmd/internal/connectivity" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/client/local" - "github.com/odpf/optimus/client/local/model" - "github.com/odpf/optimus/client/local/specio" - "github.com/odpf/optimus/config" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/client/local" + "github.com/raystack/optimus/client/local/model" + "github.com/raystack/optimus/client/local/specio" + "github.com/raystack/optimus/config" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) const ( @@ -26,7 +27,9 @@ const ( ) type exportCommand struct { - logger log.Logger + logger log.Logger + connection connection.Connection + writer local.SpecWriter[*model.JobSpec] configFilePath string @@ -84,6 +87,8 @@ func (e *exportCommand) PreRunE(_ *cobra.Command, _ []string) error { } else { e.host = cfg.Host } + e.connection = connection.New(e.logger, cfg) + return err } @@ -211,15 +216,18 @@ func (e *exportCommand) writeJobs(projectName, namespaceName string, jobs []*mod } func (e *exportCommand) fetchNamespaceJobsByProjectName(projectName string) (map[string][]*model.JobSpec, error) { - conn, err := connectivity.NewConnectivity(e.host, fetchJobTimeout) + conn, err := e.connection.Create(e.host) if err != nil { return nil, err } defer conn.Close() - jobSpecificationServiceClient := pb.NewJobSpecificationServiceClient(conn.GetConnection()) + jobSpecificationServiceClient := pb.NewJobSpecificationServiceClient(conn) + + ctx, cancelFunc := context.WithTimeout(context.Background(), fetchJobTimeout) + defer cancelFunc() - response, err := jobSpecificationServiceClient.GetJobSpecifications(conn.GetContext(), &pb.GetJobSpecificationsRequest{ + response, err := jobSpecificationServiceClient.GetJobSpecifications(ctx, &pb.GetJobSpecificationsRequest{ ProjectName: projectName, }) if err != nil { @@ -234,15 +242,18 @@ func (e *exportCommand) fetchNamespaceJobsByProjectName(projectName string) (map } func (e *exportCommand) fetchJobsByProjectAndNamespaceName(projectName, namespaceName string) ([]*model.JobSpec, error) { - conn, err := connectivity.NewConnectivity(e.host, fetchJobTimeout) + conn, err := e.connection.Create(e.host) if err != nil { return nil, err } defer conn.Close() - jobSpecificationServiceClient := pb.NewJobSpecificationServiceClient(conn.GetConnection()) + jobSpecificationServiceClient := pb.NewJobSpecificationServiceClient(conn) - response, err := jobSpecificationServiceClient.GetJobSpecifications(conn.GetContext(), &pb.GetJobSpecificationsRequest{ + ctx, cancelFunc := context.WithTimeout(context.Background(), fetchJobTimeout) + defer cancelFunc() + + response, err := jobSpecificationServiceClient.GetJobSpecifications(ctx, &pb.GetJobSpecificationsRequest{ ProjectName: projectName, NamespaceName: namespaceName, }) @@ -258,15 +269,18 @@ func (e *exportCommand) fetchJobsByProjectAndNamespaceName(projectName, namespac } func (e *exportCommand) fetchSpecificJob(projectName, namespaceName, jobName string) (*model.JobSpec, error) { - conn, err := connectivity.NewConnectivity(e.host, fetchJobTimeout) + conn, err := e.connection.Create(e.host) if err != nil { return nil, err } defer conn.Close() - jobSpecificationServiceClient := pb.NewJobSpecificationServiceClient(conn.GetConnection()) + jobSpecificationServiceClient := pb.NewJobSpecificationServiceClient(conn) + + ctx, cancelFunc := context.WithTimeout(context.Background(), fetchJobTimeout) + defer cancelFunc() - response, err := jobSpecificationServiceClient.GetJobSpecifications(conn.GetContext(), &pb.GetJobSpecificationsRequest{ + response, err := jobSpecificationServiceClient.GetJobSpecifications(ctx, &pb.GetJobSpecificationsRequest{ ProjectName: projectName, NamespaceName: namespaceName, JobName: jobName, @@ -282,15 +296,18 @@ func (e *exportCommand) fetchSpecificJob(projectName, namespaceName, jobName str } func (e *exportCommand) fetchProjectNames() ([]string, error) { - conn, err := connectivity.NewConnectivity(e.host, fetchTenantTimeout) + conn, err := e.connection.Create(e.host) if err != nil { return nil, err } defer conn.Close() - projectServiceClient := pb.NewProjectServiceClient(conn.GetConnection()) + projectServiceClient := pb.NewProjectServiceClient(conn) + + ctx, cancelFunc := context.WithTimeout(context.Background(), fetchTenantTimeout) + defer cancelFunc() - response, err := projectServiceClient.ListProjects(conn.GetContext(), &pb.ListProjectsRequest{}) + response, err := projectServiceClient.ListProjects(ctx, &pb.ListProjectsRequest{}) if err != nil { return nil, err } diff --git a/client/cmd/job/inspect.go b/client/cmd/job/inspect.go index f8ac19f7f7..6ea475c9d5 100644 --- a/client/cmd/job/inspect.go +++ b/client/cmd/job/inspect.go @@ -6,18 +6,18 @@ import ( "fmt" "time" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/afero" "github.com/spf13/cobra" "gopkg.in/yaml.v2" - "github.com/odpf/optimus/client/cmd/internal/connectivity" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/client/cmd/internal/survey" - "github.com/odpf/optimus/client/local/model" - "github.com/odpf/optimus/client/local/specio" - "github.com/odpf/optimus/config" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/client/cmd/internal/survey" + "github.com/raystack/optimus/client/local/model" + "github.com/raystack/optimus/client/local/specio" + "github.com/raystack/optimus/config" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) const ( @@ -27,7 +27,8 @@ const ( ) type inspectCommand struct { - logger log.Logger + logger log.Logger + connection connection.Connection configFilePath string @@ -68,6 +69,8 @@ func (e *inspectCommand) PreRunE(_ *cobra.Command, _ []string) error { e.logger = logger.NewClientLogger() e.jobSurvey = survey.NewJobSurvey() e.namespaceSurvey = survey.NewNamespaceSurvey(e.logger) + + e.connection = connection.New(e.logger, e.clientConfig) return nil } @@ -127,7 +130,7 @@ func (e *inspectCommand) loadConfig() error { } func (e *inspectCommand) inspectJobSpecification(jobSpec *model.JobSpec, serverFetch bool) error { - conn, err := connectivity.NewConnectivity(e.clientConfig.Host, inspectTimeout) + conn, err := e.connection.Create(e.clientConfig.Host) if err != nil { return err } @@ -147,8 +150,11 @@ func (e *inspectCommand) inspectJobSpecification(jobSpec *model.JobSpec, serverF Spec: adaptedSpec, JobName: jobName, } - job := pb.NewJobSpecificationServiceClient(conn.GetConnection()) - resp, err := job.JobInspect(conn.GetContext(), jobInspectRequest) + job := pb.NewJobSpecificationServiceClient(conn) + + ctx, dialCancel := context.WithTimeout(context.Background(), inspectTimeout) + defer dialCancel() + resp, err := job.JobInspect(ctx, jobInspectRequest) if err != nil { if errors.Is(err, context.DeadlineExceeded) { e.logger.Error("Inspect process took too long, timing out") diff --git a/client/cmd/job/job.go b/client/cmd/job/job.go index 4c3361bd88..98d022abd0 100644 --- a/client/cmd/job/job.go +++ b/client/cmd/job/job.go @@ -24,6 +24,7 @@ func NewJobCommand() *cobra.Command { NewReplaceAllCommand(), NewExportCommand(), NewJobRunInputCommand(), + NewChangeNamespaceCommand(), ) return cmd } diff --git a/client/cmd/job/refresh.go b/client/cmd/job/refresh.go index c81ceb7905..36e7f80702 100644 --- a/client/cmd/job/refresh.go +++ b/client/cmd/job/refresh.go @@ -7,14 +7,14 @@ import ( "io" "time" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/cmd/internal" - "github.com/odpf/optimus/client/cmd/internal/connectivity" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/config" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/client/cmd/internal" + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/config" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) const ( @@ -23,6 +23,7 @@ const ( type refreshCommand struct { logger log.Logger + connection connection.Connection configFilePath string verbose bool @@ -83,6 +84,8 @@ func (r *refreshCommand) PreRunE(cmd *cobra.Command, _ []string) error { if r.host == "" { r.host = conf.Host } + + r.connection = connection.New(r.logger, conf) return nil } @@ -102,15 +105,17 @@ func (r *refreshCommand) RunE(_ *cobra.Command, _ []string) error { } func (r *refreshCommand) refreshJobSpecificationRequest() error { - conn, err := connectivity.NewConnectivity(r.host, refreshTimeout) + conn, err := r.connection.Create(r.host) if err != nil { return err } defer conn.Close() - jobSpecService := pb.NewJobSpecificationServiceClient(conn.GetConnection()) + jobSpecService := pb.NewJobSpecificationServiceClient(conn) - respStream, err := jobSpecService.RefreshJobs(conn.GetContext(), &pb.RefreshJobsRequest{ + ctx, dialCancel := context.WithTimeout(context.Background(), refreshTimeout) + defer dialCancel() + respStream, err := jobSpecService.RefreshJobs(ctx, &pb.RefreshJobsRequest{ ProjectName: r.projectName, NamespaceNames: r.selectedNamespaceNames, JobNames: r.selectedJobNames, diff --git a/client/cmd/job/replace_all.go b/client/cmd/job/replace_all.go index b5ab18c24a..cb2dceabe8 100644 --- a/client/cmd/job/replace_all.go +++ b/client/cmd/job/replace_all.go @@ -9,16 +9,17 @@ import ( "time" "github.com/MakeNowJust/heredoc" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/afero" "github.com/spf13/cobra" - - "github.com/odpf/optimus/client/cmd/internal/connectivity" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/client/local/specio" - "github.com/odpf/optimus/config" - "github.com/odpf/optimus/internal/models" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "google.golang.org/grpc" + + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/client/local/specio" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/internal/models" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) const ( @@ -26,7 +27,9 @@ const ( ) type replaceAllCommand struct { - logger log.Logger + logger log.Logger + connection connection.Connection + clientConfig *config.ClientConfig selectedNamespaceNames []string @@ -64,6 +67,8 @@ func (r *replaceAllCommand) PreRunE(_ *cobra.Command, _ []string) error { if err != nil { return err } + + r.connection = connection.New(r.logger, r.clientConfig) return nil } @@ -82,7 +87,7 @@ func (r *replaceAllCommand) RunE(_ *cobra.Command, _ []string) error { } func (r *replaceAllCommand) replaceAll(selectedNamespaces []*config.Namespace) error { - conn, err := connectivity.NewConnectivity(r.clientConfig.Host, replaceAllTimeout) + conn, err := r.connection.Create(r.clientConfig.Host) if err != nil { return err } @@ -96,14 +101,17 @@ func (r *replaceAllCommand) replaceAll(selectedNamespaces []*config.Namespace) e return nil } -func (r *replaceAllCommand) replaceAllJobs(conn *connectivity.Connectivity, selectedNamespaces []*config.Namespace) error { +func (r *replaceAllCommand) replaceAllJobs(conn *grpc.ClientConn, selectedNamespaces []*config.Namespace) error { var namespaceNames []string for _, namespace := range selectedNamespaces { namespaceNames = append(namespaceNames, namespace.Name) } r.logger.Info("> Replacing all jobs for namespaces [%s]", strings.Join(namespaceNames, ",")) - stream, err := r.getJobStreamClient(conn) + ctx, dialCancel := context.WithTimeout(context.Background(), replaceAllTimeout) + defer dialCancel() + + stream, err := r.getJobStreamClient(ctx, conn) if err != nil { return err } @@ -171,11 +179,10 @@ func (*replaceAllCommand) getReplaceAllRequest(projectName string, namespace *co }, nil } -func (r *replaceAllCommand) getJobStreamClient( - conn *connectivity.Connectivity, -) (pb.JobSpecificationService_ReplaceAllJobSpecificationsClient, error) { - client := pb.NewJobSpecificationServiceClient(conn.GetConnection()) - stream, err := client.ReplaceAllJobSpecifications(conn.GetContext()) +func (r *replaceAllCommand) getJobStreamClient(ctx context.Context, conn *grpc.ClientConn) (pb.JobSpecificationService_ReplaceAllJobSpecificationsClient, error) { + client := pb.NewJobSpecificationServiceClient(conn) + + stream, err := client.ReplaceAllJobSpecifications(ctx) if err != nil { if errors.Is(err, context.DeadlineExceeded) { r.logger.Error("Replace job specifications process took too long, timing out") diff --git a/client/cmd/job/run_input.go b/client/cmd/job/run_input.go index aef630b795..d57e83d23b 100644 --- a/client/cmd/job/run_input.go +++ b/client/cmd/job/run_input.go @@ -1,6 +1,7 @@ package job import ( + "context" "fmt" "io/fs" "os" @@ -8,16 +9,16 @@ import ( "strings" "time" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/cobra" "google.golang.org/protobuf/types/known/timestamppb" - "github.com/odpf/optimus/client/cmd/internal" - "github.com/odpf/optimus/client/cmd/internal/connectivity" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/config" - "github.com/odpf/optimus/internal/utils" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/client/cmd/internal" + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/internal/utils" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) const ( @@ -33,7 +34,9 @@ const ( ) type jobRunInputCommand struct { - logger log.Logger + logger log.Logger + connection *connection.Insecure + configFilePath string assetOutputDir string @@ -103,6 +106,9 @@ func (j *jobRunInputCommand) PreRunE(cmd *cobra.Command, _ []string) error { if j.host == "" { j.host = conf.Host } + + j.connection = connection.NewInsecure(j.logger) + return nil } @@ -209,14 +215,14 @@ func (j *jobRunInputCommand) writeJobAssetsToFiles( } func (j *jobRunInputCommand) sendJobRunInputRequest(jobName string, jobScheduledTimeProto *timestamppb.Timestamp) (*pb.JobRunInputResponse, error) { - conn, err := connectivity.NewConnectivity(j.host, jobRunInputCompileAssetsTimeout) + conn, err := j.connection.Create(j.host) if err != nil { return nil, err } defer conn.Close() // fetch Instance by calling the optimus API - jobRunServiceClient := pb.NewJobRunServiceClient(conn.GetConnection()) + jobRunServiceClient := pb.NewJobRunServiceClient(conn) request := &pb.JobRunInputRequest{ ProjectName: j.projectName, JobName: jobName, @@ -225,7 +231,10 @@ func (j *jobRunInputCommand) sendJobRunInputRequest(jobName string, jobScheduled InstanceName: j.runName, } - return jobRunServiceClient.JobRunInput(conn.GetContext(), request) + ctx, reqCancel := context.WithTimeout(context.Background(), jobRunInputCompileAssetsTimeout) + defer reqCancel() + + return jobRunServiceClient.JobRunInput(ctx, request) } func (j *jobRunInputCommand) getJobScheduledTimeProto() (*timestamppb.Timestamp, error) { diff --git a/client/cmd/job/run_list.go b/client/cmd/job/run_list.go index f1ac5bccf8..8aadd3d210 100644 --- a/client/cmd/job/run_list.go +++ b/client/cmd/job/run_list.go @@ -1,26 +1,28 @@ package job import ( + "context" "errors" "fmt" "time" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/cobra" "google.golang.org/protobuf/types/known/timestamppb" - "github.com/odpf/optimus/client/cmd/internal" - "github.com/odpf/optimus/client/cmd/internal/connectivity" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/client/cmd/internal/progressbar" - "github.com/odpf/optimus/config" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/client/cmd/internal" + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/client/cmd/internal/progressbar" + "github.com/raystack/optimus/config" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) const jobStatusTimeout = time.Second * 30 type runListCommand struct { logger log.Logger + connection *connection.Insecure configFilePath string startDate string @@ -97,7 +99,7 @@ func (r *runListCommand) RunE(_ *cobra.Command, args []string) error { } func (r *runListCommand) callJobRun(jobRunRequest *pb.JobRunRequest) error { - conn, err := connectivity.NewConnectivity(r.host, jobStatusTimeout) + conn, err := r.connection.Create(r.host) if err != nil { return err } @@ -105,8 +107,12 @@ func (r *runListCommand) callJobRun(jobRunRequest *pb.JobRunRequest) error { spinner := progressbar.NewProgressBar() spinner.Start("please wait...") - run := pb.NewJobRunServiceClient(conn.GetConnection()) - jobRunResponse, err := run.JobRun(conn.GetContext(), jobRunRequest) + run := pb.NewJobRunServiceClient(conn) + + ctx, dialCancel := context.WithTimeout(context.Background(), jobStatusTimeout) + defer dialCancel() + + jobRunResponse, err := run.JobRun(ctx, jobRunRequest) spinner.Stop() if err != nil { return fmt.Errorf("request failed for job %s: %w", jobRunRequest.JobName, err) diff --git a/client/cmd/job/validate.go b/client/cmd/job/validate.go index c2bfd87ec0..f5e4bbe17a 100644 --- a/client/cmd/job/validate.go +++ b/client/cmd/job/validate.go @@ -7,22 +7,24 @@ import ( "io" "time" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/afero" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/cmd/internal/connectivity" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/client/local/model" - "github.com/odpf/optimus/client/local/specio" - "github.com/odpf/optimus/config" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/client/local/model" + "github.com/raystack/optimus/client/local/specio" + "github.com/raystack/optimus/config" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) const validateTimeout = time.Minute * 5 type validateCommand struct { - logger log.Logger + logger log.Logger + connection *connection.Insecure + configFilePath string clientConfig *config.ClientConfig @@ -59,6 +61,8 @@ func (v *validateCommand) PreRunE(_ *cobra.Command, _ []string) error { // Load return err } v.clientConfig = conf + + v.connection = connection.NewInsecure(v.logger) return nil } @@ -88,7 +92,7 @@ func (v *validateCommand) RunE(_ *cobra.Command, _ []string) error { } func (v *validateCommand) validateJobSpecificationRequest(jobSpecs []*model.JobSpec) error { - conn, err := connectivity.NewConnectivity(v.clientConfig.Host, validateTimeout) + conn, err := v.connection.Create(v.clientConfig.Host) if err != nil { return err } @@ -104,8 +108,12 @@ func (v *validateCommand) validateJobSpecificationRequest(jobSpecs []*model.JobS Jobs: jobSpecsProto, NamespaceName: v.namespaceName, } - job := pb.NewJobSpecificationServiceClient(conn.GetConnection()) - respStream, err := job.CheckJobSpecifications(conn.GetContext(), checkJobSpecRequest) + job := pb.NewJobSpecificationServiceClient(conn) + + ctx, dialCancel := context.WithTimeout(context.Background(), validateTimeout) + defer dialCancel() + + respStream, err := job.CheckJobSpecifications(ctx, checkJobSpecRequest) if err != nil { if errors.Is(err, context.DeadlineExceeded) { v.logger.Error("Validate process took too long, timing out") diff --git a/client/cmd/namespace/describe.go b/client/cmd/namespace/describe.go index f3fb29149f..a03b60d7cf 100644 --- a/client/cmd/namespace/describe.go +++ b/client/cmd/namespace/describe.go @@ -1,24 +1,27 @@ package namespace import ( + "context" "fmt" "path" "time" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/cmd/internal" - "github.com/odpf/optimus/client/cmd/internal/connectivity" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/config" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/client/cmd/internal" + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/config" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) const describeTimeout = time.Minute * 15 type describeCommand struct { - logger log.Logger + logger log.Logger + connection connection.Connection + configFilePath string dirPath string @@ -79,6 +82,7 @@ func (d *describeCommand) PreRunE(cmd *cobra.Command, _ []string) error { if d.host == "" { d.host = conf.Host } + d.connection = connection.New(d.logger, conf) return nil } @@ -99,7 +103,7 @@ func (d *describeCommand) RunE(_ *cobra.Command, _ []string) error { } func (d *describeCommand) getNamespace() (*config.Namespace, error) { - conn, err := connectivity.NewConnectivity(d.host, describeTimeout) + conn, err := d.connection.Create(d.host) if err != nil { return nil, err } @@ -109,8 +113,12 @@ func (d *describeCommand) getNamespace() (*config.Namespace, error) { ProjectName: d.projectName, NamespaceName: d.namespaceName, } - namespaceServiceClient := pb.NewNamespaceServiceClient(conn.GetConnection()) - response, err := namespaceServiceClient.GetNamespace(conn.GetContext(), request) + namespaceServiceClient := pb.NewNamespaceServiceClient(conn) + + ctx, cancelFunc := context.WithTimeout(context.Background(), describeTimeout) + defer cancelFunc() + + response, err := namespaceServiceClient.GetNamespace(ctx, request) if err != nil { return nil, fmt.Errorf("unable to get namespace [%s]: %w", d.namespaceName, err) } diff --git a/client/cmd/namespace/list.go b/client/cmd/namespace/list.go index 3b0096904c..d246c60ba8 100644 --- a/client/cmd/namespace/list.go +++ b/client/cmd/namespace/list.go @@ -2,25 +2,28 @@ package namespace import ( "bytes" + "context" "fmt" "path" "time" - "github.com/odpf/salt/log" "github.com/olekukonko/tablewriter" + "github.com/raystack/salt/log" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/cmd/internal" - "github.com/odpf/optimus/client/cmd/internal/connectivity" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/config" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/client/cmd/internal" + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/config" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) const listTimeout = time.Minute * 15 type listCommand struct { - logger log.Logger + logger log.Logger + connection connection.Connection + configFilePath string clientConfig *config.ClientConfig @@ -79,6 +82,8 @@ func (l *listCommand) PreRunE(cmd *cobra.Command, _ []string) error { if l.host == "" { l.host = l.clientConfig.Host } + l.connection = connection.New(l.logger, conf) + return nil } @@ -98,8 +103,8 @@ func (l *listCommand) RunE(_ *cobra.Command, _ []string) error { return nil } -func (*listCommand) listNamespacesFromServer(serverHost, projectName string) ([]*config.Namespace, error) { - conn, err := connectivity.NewConnectivity(serverHost, listTimeout) +func (l *listCommand) listNamespacesFromServer(serverHost, projectName string) ([]*config.Namespace, error) { + conn, err := l.connection.Create(serverHost) if err != nil { return nil, err } @@ -108,8 +113,12 @@ func (*listCommand) listNamespacesFromServer(serverHost, projectName string) ([] request := &pb.ListProjectNamespacesRequest{ ProjectName: projectName, } - namespaceServiceClient := pb.NewNamespaceServiceClient(conn.GetConnection()) - response, err := namespaceServiceClient.ListProjectNamespaces(conn.GetContext(), request) + namespaceServiceClient := pb.NewNamespaceServiceClient(conn) + + ctx, cancelFunc := context.WithTimeout(context.Background(), listTimeout) + defer cancelFunc() + + response, err := namespaceServiceClient.ListProjectNamespaces(ctx, request) if err != nil { return nil, fmt.Errorf("unable to list namespace for project [%s]: %w", projectName, err) } diff --git a/client/cmd/namespace/namespace.go b/client/cmd/namespace/namespace.go index 28f6dd7004..7ef0f336d1 100644 --- a/client/cmd/namespace/namespace.go +++ b/client/cmd/namespace/namespace.go @@ -18,11 +18,3 @@ func NewNamespaceCommand() *cobra.Command { ) return cmd } - -// GetAllowedDownstreamNamespaces gets all downstream namespace names -func GetAllowedDownstreamNamespaces(namespaceName string, allDownstream bool) []string { - if allDownstream { - return []string{"*"} - } - return []string{namespaceName} -} diff --git a/client/cmd/namespace/register.go b/client/cmd/namespace/register.go index 951676a31b..8af93c8149 100644 --- a/client/cmd/namespace/register.go +++ b/client/cmd/namespace/register.go @@ -1,20 +1,22 @@ package namespace import ( + "context" "errors" "fmt" "path" "time" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/cobra" + "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "github.com/odpf/optimus/client/cmd/internal/connectivity" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/config" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/config" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) const registerTimeout = time.Minute * 15 @@ -49,26 +51,34 @@ func (r *registerCommand) RunE(_ *cobra.Command, _ []string) error { if err != nil { return err } + + conn := connection.New(r.logger, clientConfig) + c, err := conn.Create(clientConfig.Host) + if err != nil { + return err + } + defer c.Close() + if r.namespaceName != "" { r.logger.Info("Registering namespace [%s] to [%s]", r.namespaceName, clientConfig.Host) namespace, err := clientConfig.GetNamespaceByName(r.namespaceName) if err != nil { return err } - return RegisterNamespace(r.logger, clientConfig.Host, clientConfig.Project.Name, namespace) + return RegisterNamespace(r.logger, c, clientConfig.Project.Name, namespace) } r.logger.Info("Registering all available namespaces from client config to [%s]", clientConfig.Host) - return RegisterSelectedNamespaces(r.logger, clientConfig.Host, clientConfig.Project.Name, clientConfig.Namespaces...) + return RegisterSelectedNamespaces(r.logger, c, clientConfig.Project.Name, clientConfig.Namespaces...) } // RegisterSelectedNamespaces registers all selected namespaces -func RegisterSelectedNamespaces(l log.Logger, serverHost, projectName string, selectedNamespaces ...*config.Namespace) error { +func RegisterSelectedNamespaces(l log.Logger, conn *grpc.ClientConn, projectName string, selectedNamespaces ...*config.Namespace) error { ch := make(chan error, len(selectedNamespaces)) defer close(ch) for _, namespace := range selectedNamespaces { go func(namespace *config.Namespace) { - ch <- RegisterNamespace(l, serverHost, projectName, namespace) + ch <- RegisterNamespace(l, conn, projectName, namespace) }(namespace) } var errMsg string @@ -84,15 +94,13 @@ func RegisterSelectedNamespaces(l log.Logger, serverHost, projectName string, se } // RegisterNamespace registers one namespace to the targeted server -func RegisterNamespace(l log.Logger, serverHost, projectName string, namespace *config.Namespace) error { - conn, err := connectivity.NewConnectivity(serverHost, registerTimeout) - if err != nil { - return err - } - defer conn.Close() +func RegisterNamespace(l log.Logger, conn *grpc.ClientConn, projectName string, namespace *config.Namespace) error { + namespaceServiceClient := pb.NewNamespaceServiceClient(conn) + + ctx, cancelFunc := context.WithTimeout(context.Background(), registerTimeout) + defer cancelFunc() - namespaceServiceClient := pb.NewNamespaceServiceClient(conn.GetConnection()) - _, err = namespaceServiceClient.RegisterProjectNamespace(conn.GetContext(), &pb.RegisterProjectNamespaceRequest{ + _, err := namespaceServiceClient.RegisterProjectNamespace(ctx, &pb.RegisterProjectNamespaceRequest{ ProjectName: projectName, Namespace: &pb.NamespaceSpecification{ Name: namespace.Name, diff --git a/client/cmd/playground/playground.go b/client/cmd/playground/playground.go index 43f9afd845..81e255dc44 100644 --- a/client/cmd/playground/playground.go +++ b/client/cmd/playground/playground.go @@ -3,7 +3,7 @@ package playground import ( "github.com/spf13/cobra" - "github.com/odpf/optimus/client/cmd/playground/window" + "github.com/raystack/optimus/client/cmd/playground/window" ) // NewPlaygroundCommand initializes command for playground diff --git a/client/cmd/playground/window/model.go b/client/cmd/playground/window/model.go index 8c895a5bfb..a1833d8a3f 100644 --- a/client/cmd/playground/window/model.go +++ b/client/cmd/playground/window/model.go @@ -10,14 +10,12 @@ import ( "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" - "github.com/odpf/salt/log" "github.com/olekukonko/tablewriter" - "github.com/odpf/optimus/internal/models" + "github.com/raystack/optimus/internal/models" ) type model struct { - log log.Logger currentCursor cursorPointer truncateTo truncateTo @@ -27,18 +25,12 @@ type model struct { scheduledTime time.Time } -func newModel(log log.Logger) *model { - offsetInput := textinput.New() - - sizeInput := textinput.New() - sizeInput.Focus() - +func newModel() *model { return &model{ - log: log, currentCursor: pointToTruncateTo, truncateTo: truncateToDay, - sizeInput: sizeInput, - offsetInput: offsetInput, + sizeInput: textinput.New(), + offsetInput: textinput.New(), scheduledTime: time.Now(), } } @@ -53,6 +45,7 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if currMsg.String() != "tea.KeyMsg" { return m, nil } + msgStr := fmt.Sprintf("%s", msg) switch msgStr { case "ctrl+c", "q": @@ -73,8 +66,7 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "backspace": - m.sizeInput, _ = m.sizeInput.Update(msg) - m.offsetInput, _ = m.offsetInput.Update(msg) + m.handleInput(msg) } return m, nil } @@ -92,7 +84,7 @@ func (m *model) View() string { s.WriteString(m.generateWindowResultView()) s.WriteString("\n") s.WriteString("DOCUMENTATION:\n") - s.WriteString("- https://odpf.github.io/optimus/docs/concepts/intervals-and-windows") + s.WriteString("- https://raystack.github.io/optimus/docs/concepts/intervals-and-windows") return s.String() } @@ -100,7 +92,6 @@ func (m *model) generateWindowResultView() string { buff := &bytes.Buffer{} table := tablewriter.NewWriter(buff) table.SetHeader([]string{"Version", "Start Time", "End Time"}) - table.SetAutoMergeCells(true) table.Append(m.generateWindowTableRowView(1)) table.Append(m.generateWindowTableRowView(2)) //nolint: gomnd table.Render() @@ -172,6 +163,7 @@ func (m *model) generateWindowInputView() string { buff := &bytes.Buffer{} table := tablewriter.NewWriter(buff) table.SetRowLine(true) + table.SetColumnAlignment([]int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT}) table.Append([]string{ "truncate_to", m.generateValueWithCursorPointerView(pointToTruncateTo, string(m.truncateTo)), @@ -195,14 +187,16 @@ func (m *model) generateWindowInputView() string { func (m *model) generateWindowTableRowView(version int) []string { window, err := models.NewWindow(version, string(m.truncateTo), m.offsetInput.Value(), m.sizeInput.Value()) if err != nil { - return []string{fmt.Sprintf("%d", version), err.Error()} + return []string{fmt.Sprintf("%d", version), err.Error(), err.Error()} } + var startTimeRow string if startTime, err := window.GetStartTime(m.scheduledTime); err != nil { startTimeRow = err.Error() } else { startTimeRow = startTime.Format(time.RFC3339) } + var endTimeRow string if endTime, err := window.GetEndTime(m.scheduledTime); err != nil { endTimeRow = err.Error() @@ -232,6 +226,15 @@ func (m *model) generateValueWithCursorPointerView(targetCursor cursorPointer, v return value } +func (m *model) handleInput(msg tea.Msg) { + switch m.currentCursor { + case pointToOffset: + m.offsetInput, _ = m.offsetInput.Update(msg) + case pointToSize: + m.sizeInput, _ = m.sizeInput.Update(msg) + } +} + func (m *model) handleDecrement() { switch m.currentCursor { case pointToTruncateTo: diff --git a/client/cmd/playground/window/window.go b/client/cmd/playground/window/window.go index 4907ff301a..cad1709781 100644 --- a/client/cmd/playground/window/window.go +++ b/client/cmd/playground/window/window.go @@ -2,10 +2,10 @@ package window import ( tea "github.com/charmbracelet/bubbletea" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/client/cmd/internal/logger" ) type command struct { @@ -27,6 +27,6 @@ func (j *command) RunE(_ *cobra.Command, _ []string) error { j.log.Info("Hi, this is an interactive CLI to play around with window configuration.") j.log.Info("Navigate around the available configurations input with arrow keys.") j.log.Info("If you want to quit, just press 'q' or 'ctr+c' key.\n") - p := tea.NewProgram(newModel(j.log)) + p := tea.NewProgram(newModel()) return p.Start() } diff --git a/client/cmd/plugin/install.go b/client/cmd/plugin/install.go index 67c822cbfe..58d0635bbe 100644 --- a/client/cmd/plugin/install.go +++ b/client/cmd/plugin/install.go @@ -1,12 +1,12 @@ package plugin import ( - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/config" - "github.com/odpf/optimus/plugin" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/plugin" ) type installCommand struct { diff --git a/client/cmd/plugin/sync.go b/client/cmd/plugin/sync.go index 69074d5b7a..7cc80d82ff 100644 --- a/client/cmd/plugin/sync.go +++ b/client/cmd/plugin/sync.go @@ -9,12 +9,12 @@ import ( "os" "strings" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/config" - "github.com/odpf/optimus/plugin" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/plugin" ) type syncCommand struct { diff --git a/client/cmd/plugin/validate.go b/client/cmd/plugin/validate.go index 03e2632bc8..b2969c83e1 100644 --- a/client/cmd/plugin/validate.go +++ b/client/cmd/plugin/validate.go @@ -8,11 +8,11 @@ import ( "path/filepath" "strings" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/plugin/yaml" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/plugin/yaml" ) type validateCommand struct { diff --git a/client/cmd/project/describe.go b/client/cmd/project/describe.go index 52ba005e96..f0f9a2b21c 100644 --- a/client/cmd/project/describe.go +++ b/client/cmd/project/describe.go @@ -1,24 +1,27 @@ package project import ( + "context" "path" "time" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/cobra" "gopkg.in/yaml.v2" - "github.com/odpf/optimus/client/cmd/internal" - "github.com/odpf/optimus/client/cmd/internal/connectivity" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/config" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/client/cmd/internal" + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/config" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) const describeTimeout = time.Minute * 15 type describeCommand struct { - logger log.Logger + logger log.Logger + connection connection.Connection + configFilePath string dirPath string @@ -75,6 +78,8 @@ func (d *describeCommand) PreRunE(cmd *cobra.Command, _ []string) error { if d.host == "" { d.host = conf.Host } + d.connection = connection.New(d.logger, conf) + return nil } @@ -95,7 +100,7 @@ func (d *describeCommand) RunE(_ *cobra.Command, _ []string) error { func (d *describeCommand) getProject() (config.Project, error) { var project config.Project - conn, err := connectivity.NewConnectivity(d.host, describeTimeout) + conn, err := d.connection.Create(d.host) if err != nil { return project, err } @@ -105,8 +110,12 @@ func (d *describeCommand) getProject() (config.Project, error) { ProjectName: d.projectName, } - projectServiceClient := pb.NewProjectServiceClient(conn.GetConnection()) - response, err := projectServiceClient.GetProject(conn.GetContext(), request) + projectServiceClient := pb.NewProjectServiceClient(conn) + + ctx, cancelFunc := context.WithTimeout(context.Background(), describeTimeout) + defer cancelFunc() + + response, err := projectServiceClient.GetProject(ctx, request) if err != nil { return project, err } diff --git a/client/cmd/project/register.go b/client/cmd/project/register.go index 8f3be05475..acde909913 100644 --- a/client/cmd/project/register.go +++ b/client/cmd/project/register.go @@ -1,20 +1,22 @@ package project import ( + "context" "fmt" "path" "time" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/cobra" + "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "github.com/odpf/optimus/client/cmd/internal/connectivity" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/client/cmd/namespace" - "github.com/odpf/optimus/config" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/client/cmd/namespace" + "github.com/raystack/optimus/config" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) const registerTimeout = time.Minute * 15 @@ -49,13 +51,21 @@ func (r *registerCommand) RunE(_ *cobra.Command, _ []string) error { if err != nil { return err } + + conn := connection.New(r.logger, clientConfig) + c, err := conn.Create(clientConfig.Host) + if err != nil { + return err + } + defer c.Close() + r.logger.Info("Registering project [%s] to server [%s]", clientConfig.Project.Name, clientConfig.Host) - if err := RegisterProject(r.logger, clientConfig.Host, clientConfig.Project); err != nil { + if err := RegisterProject(r.logger, c, clientConfig.Project); err != nil { return err } if r.withNamespaces { r.logger.Info("Registering all namespaces from: %s", filePath) - if err := namespace.RegisterSelectedNamespaces(r.logger, clientConfig.Host, clientConfig.Project.Name, clientConfig.Namespaces...); err != nil { + if err := namespace.RegisterSelectedNamespaces(r.logger, c, clientConfig.Project.Name, clientConfig.Namespaces...); err != nil { return err } } @@ -63,22 +73,19 @@ func (r *registerCommand) RunE(_ *cobra.Command, _ []string) error { } // RegisterProject registers a project to the targeted server host -func RegisterProject(logger log.Logger, serverHost string, project config.Project) error { - conn, err := connectivity.NewConnectivity(serverHost, registerTimeout) - if err != nil { - return err - } - defer conn.Close() - - projectServiceClient := pb.NewProjectServiceClient(conn.GetConnection()) +func RegisterProject(logger log.Logger, conn *grpc.ClientConn, project config.Project) error { + projectServiceClient := pb.NewProjectServiceClient(conn) projectSpec := &pb.ProjectSpecification{ Name: project.Name, Config: project.Config, } - _, err = projectServiceClient.RegisterProject(conn.GetContext(), &pb.RegisterProjectRequest{ + + ctx, cancelFunc := context.WithTimeout(context.Background(), registerTimeout) + defer cancelFunc() + + _, err := projectServiceClient.RegisterProject(ctx, &pb.RegisterProjectRequest{ Project: projectSpec, }) - if err != nil { if status.Code(err) == codes.FailedPrecondition { logger.Warn(fmt.Sprintf("Ignoring project config changes: %v", err)) diff --git a/client/cmd/replay/create.go b/client/cmd/replay/create.go index 0b04a3c44c..a7d531e4bf 100644 --- a/client/cmd/replay/create.go +++ b/client/cmd/replay/create.go @@ -5,25 +5,34 @@ import ( "fmt" "time" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/cobra" "golang.org/x/net/context" "google.golang.org/protobuf/types/known/timestamppb" - "github.com/odpf/optimus/client/cmd/internal" - "github.com/odpf/optimus/client/cmd/internal/connectivity" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/config" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/client/cmd/internal" + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/client/cmd/internal/progressbar" + "github.com/raystack/optimus/config" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) const ( - replayTimeout = time.Minute * 1 - ISOTimeLayout = time.RFC3339 + replayTimeout = time.Minute * 1 + ISOTimeLayout = time.RFC3339 + pollIntervalInSecond = 30 +) + +var ( + supportedISOTimeLayouts = [...]string{time.RFC3339, "2006-01-02"} + terminalStatuses = map[string]bool{"success": true, "failed": true, "invalid": true} ) type createCommand struct { - logger log.Logger + logger log.Logger + connection connection.Connection + configFilePath string parallel bool @@ -45,8 +54,9 @@ func CreateCommand() *cobra.Command { Use: "create", Short: "Run replay operation on a dag based on provided start and end time range", Long: "This operation takes three arguments, first is DAG name[required]\nused in optimus specification, " + - "second is start time[required] of\nreplay, third is end time[optional] of replay. \nDate ranges are inclusive.", - Example: "optimus replay create <2023-01-01T02:30:00Z00:00> [2023-01-02T02:30:00Z00:00]", + "second is start time[required] of\nreplay, third is end time[optional] of replay. \nDate ranges are inclusive. " + + "Supported date formats are RFC3339 and \nsimple date YYYY-MM-DD", + Example: "optimus replay create <2023-01-01T02:30:00Z00:00> [2023-01-02T02:30:00Z00:00]\noptimus replay create <2023-01-01> [2023-01-02]", Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { return errors.New("job name is required") @@ -95,6 +105,8 @@ func (r *createCommand) PreRunE(cmd *cobra.Command, _ []string) error { if r.host == "" { r.host = conf.Host } + + r.connection = connection.New(r.logger, conf) return nil } @@ -110,18 +122,49 @@ func (r *createCommand) RunE(_ *cobra.Command, args []string) error { if err != nil { return err } - r.logger.Info("Replay request created with id %s", replayID) + r.logger.Info("Replay request is accepted and it is in progress") + r.logger.Info("Either you could wait or you could close (ctrl+c) and check the status with `optimus replay status %s` command later", replayID) + + return r.waitForReplayState(replayID) +} + +func (r *createCommand) waitForReplayState(replayID string) error { + spinner := progressbar.NewProgressBarWithWriter(r.logger.Writer()) + status := "in progress" + spinner.Start(fmt.Sprintf("%s...", status)) + for { + resp, err := r.getReplay(replayID) + if err != nil { + return err + } + if status != resp.Status { + status = resp.Status + spinner.StartNewLine(fmt.Sprintf("%s...", status)) + } + if _, ok := terminalStatuses[status]; ok { + spinner.StartNewLine("\n") + spinner.Stop() + r.logger.Info("\n" + stringifyReplayStatus(resp)) + break + } + time.Sleep(time.Duration(pollIntervalInSecond) * time.Second) + } + spinner.Stop() return nil } +func (r *createCommand) getReplay(replayID string) (*pb.GetReplayResponse, error) { + return getReplay(r.host, replayID, r.connection) +} + func (r *createCommand) createReplayRequest(jobName, startTimeStr, endTimeStr, jobConfig string) (string, error) { - conn, err := connectivity.NewConnectivity(r.host, replayTimeout) + conn, err := r.connection.Create(r.host) if err != nil { return "", err } defer conn.Close() - replayService := pb.NewReplayServiceClient(conn.GetConnection()) + replayService := pb.NewReplayServiceClient(conn) startTime, err := getTimeProto(startTimeStr) if err != nil { @@ -131,7 +174,11 @@ func (r *createCommand) createReplayRequest(jobName, startTimeStr, endTimeStr, j if err != nil { return "", err } - respStream, err := replayService.Replay(conn.GetContext(), &pb.ReplayRequest{ + + ctx, cancelFunc := context.WithTimeout(context.Background(), replayTimeout) + defer cancelFunc() + + respStream, err := replayService.Replay(ctx, &pb.ReplayRequest{ ProjectName: r.projectName, JobName: jobName, NamespaceName: r.namespaceName, @@ -151,7 +198,14 @@ func (r *createCommand) createReplayRequest(jobName, startTimeStr, endTimeStr, j } func getTimeProto(timeStr string) (*timestamppb.Timestamp, error) { - parsedTime, err := time.Parse(ISOTimeLayout, timeStr) + var parsedTime time.Time + var err error + for _, ISOTimeLayout := range supportedISOTimeLayouts { + parsedTime, err = time.Parse(ISOTimeLayout, timeStr) + if err == nil { + break + } + } if err != nil { return nil, err } diff --git a/client/cmd/replay/list.go b/client/cmd/replay/list.go new file mode 100644 index 0000000000..690a244e48 --- /dev/null +++ b/client/cmd/replay/list.go @@ -0,0 +1,133 @@ +package replay + +import ( + "bytes" + "context" + "time" + + "github.com/olekukonko/tablewriter" + "github.com/raystack/salt/log" + "github.com/spf13/cobra" + + "github.com/raystack/optimus/client/cmd/internal" + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/config" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" +) + +type listCommand struct { + logger log.Logger + connection connection.Connection + + configFilePath string + + projectName string + host string +} + +func ListCommand() *cobra.Command { + l := &listCommand{ + logger: logger.NewClientLogger(), + } + cmd := &cobra.Command{ + Use: "list", + Short: "List down all of the replay based on the given project", + Example: "optimus list", + PreRunE: l.PreRunE, + RunE: l.RunE, + } + l.injectFlags(cmd) + return cmd +} + +func (l *listCommand) injectFlags(cmd *cobra.Command) { + // Config filepath flag + cmd.Flags().StringVarP(&l.configFilePath, "config", "c", config.EmptyPath, "File path for client configuration") + + // Mandatory flags if config is not set + cmd.Flags().StringVarP(&l.projectName, "project-name", "p", "", "Name of the optimus project") + cmd.Flags().StringVar(&l.host, "host", "", "Optimus service endpoint url") +} + +func (l *listCommand) PreRunE(cmd *cobra.Command, _ []string) error { + conf, err := internal.LoadOptionalConfig(l.configFilePath) + if err != nil { + return err + } + + if conf == nil { + internal.MarkFlagsRequired(cmd, []string{"project-name", "host"}) + return nil + } + + if l.projectName == "" { + l.projectName = conf.Project.Name + } + if l.host == "" { + l.host = conf.Host + } + l.connection = connection.New(l.logger, conf) + return nil +} + +func (l *listCommand) RunE(_ *cobra.Command, _ []string) error { + listReplayRequest := &pb.ListReplayRequest{ + ProjectName: l.projectName, + } + return l.listReplay(listReplayRequest) +} + +func (l *listCommand) listReplay(req *pb.ListReplayRequest) error { + conn, err := l.connection.Create(l.host) + if err != nil { + return err + } + defer conn.Close() + + replayService := pb.NewReplayServiceClient(conn) + + ctx, cancelFunc := context.WithTimeout(context.Background(), replayTimeout) + defer cancelFunc() + + listReplayResp, err := replayService.ListReplay(ctx, req) + if err != nil { + return err + } + + if len(listReplayResp.GetReplays()) == 0 { + l.logger.Info("No replays were found in %s project.", req.ProjectName) + } else { + result := stringifyListOfReplays(listReplayResp) + l.logger.Info("Replays for project: %s", l.projectName) + l.logger.Info(result) + } + return nil +} + +func stringifyListOfReplays(resp *pb.ListReplayResponse) string { + buff := &bytes.Buffer{} + table := tablewriter.NewWriter(buff) + table.SetBorder(false) + table.SetHeader([]string{ + "ID", + "Job Name", + "Start Date", + "End Date", + "Description", + "Status", + }) + table.SetAlignment(tablewriter.ALIGN_LEFT) + for _, replay := range resp.Replays { + table.Append([]string{ + replay.GetId(), + replay.GetJobName(), + replay.GetReplayConfig().GetStartTime().AsTime().Format(time.RFC3339), + replay.GetReplayConfig().GetEndTime().AsTime().Format(time.RFC3339), + replay.GetReplayConfig().GetDescription(), + replay.GetStatus(), + }) + } + table.Render() + return buff.String() +} diff --git a/client/cmd/replay/replay.go b/client/cmd/replay/replay.go index e64fc80dda..0a6149c692 100644 --- a/client/cmd/replay/replay.go +++ b/client/cmd/replay/replay.go @@ -16,6 +16,8 @@ func NewReplayCommand() *cobra.Command { cmd.AddCommand( CreateCommand(), + ListCommand(), + StatusCommand(), ) return cmd } diff --git a/client/cmd/replay/status.go b/client/cmd/replay/status.go new file mode 100644 index 0000000000..51e8081361 --- /dev/null +++ b/client/cmd/replay/status.go @@ -0,0 +1,174 @@ +package replay + +import ( + "bytes" + "context" + "errors" + "fmt" + "time" + + "github.com/olekukonko/tablewriter" + "github.com/raystack/salt/log" + "github.com/spf13/cobra" + + "github.com/raystack/optimus/client/cmd/internal" + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/config" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" +) + +type statusCommand struct { + logger log.Logger + connection connection.Connection + + configFilePath string + + projectName string + host string +} + +// StatusCommand get status for corresponding replay +func StatusCommand() *cobra.Command { + status := &statusCommand{ + logger: logger.NewClientLogger(), + } + + cmd := &cobra.Command{ + Use: "status", + Short: "Get replay detailed status by replay ID", + Long: "This operation takes 1 argument, replayID [required] \nwhich UUID format ", + Example: "optimus replay status ", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("replayID is required") + } + return nil + }, + RunE: status.RunE, + PreRunE: status.PreRunE, + } + + status.injectFlags(cmd) + return cmd +} + +func (r *statusCommand) injectFlags(cmd *cobra.Command) { + // Config filepath flag + cmd.Flags().StringVarP(&r.configFilePath, "config", "c", config.EmptyPath, "File path for client configuration") + + // Mandatory flags if config is not set + cmd.Flags().StringVarP(&r.projectName, "project-name", "p", "", "Name of the optimus project") + cmd.Flags().StringVar(&r.host, "host", "", "Optimus service endpoint url") +} + +func (r *statusCommand) PreRunE(cmd *cobra.Command, _ []string) error { + conf, err := internal.LoadOptionalConfig(r.configFilePath) + if err != nil { + return err + } + + if conf == nil { + internal.MarkFlagsRequired(cmd, []string{"project-name", "host"}) + return nil + } + + if r.projectName == "" { + r.projectName = conf.Project.Name + } + if r.host == "" { + r.host = conf.Host + } + r.connection = connection.New(r.logger, conf) + return nil +} + +func (r *statusCommand) RunE(_ *cobra.Command, args []string) error { + replayID := args[0] + resp, err := r.getReplay(replayID) + if err != nil { + return err + } + result := stringifyReplayStatus(resp) + r.logger.Info("Replay status for replay ID: %s", replayID) + r.logger.Info(result) + return nil +} + +func (r *statusCommand) getReplay(replayID string) (*pb.GetReplayResponse, error) { + return getReplay(r.host, replayID, r.connection) +} + +func getReplay(host, replayID string, connection connection.Connection) (*pb.GetReplayResponse, error) { + conn, err := connection.Create(host) + if err != nil { + return nil, err + } + defer conn.Close() + + req := &pb.GetReplayRequest{ReplayId: replayID} + + replayService := pb.NewReplayServiceClient(conn) + + ctx, cancelFunc := context.WithTimeout(context.Background(), replayTimeout) + defer cancelFunc() + + return replayService.GetReplay(ctx, req) +} + +func stringifyReplayStatus(resp *pb.GetReplayResponse) string { + buff := &bytes.Buffer{} + mode := "sequential" + if resp.GetReplayConfig().GetParallel() { + mode = "parallel" + } + buff.WriteString(fmt.Sprintf("Job Name : %s\n", resp.GetJobName())) + buff.WriteString(fmt.Sprintf("Replay Mode : %s\n", mode)) + buff.WriteString(fmt.Sprintf("Description : %s\n", resp.ReplayConfig.GetDescription())) + buff.WriteString(fmt.Sprintf("Start Date : %s\n", resp.ReplayConfig.GetStartTime().AsTime().Format(time.RFC3339))) + buff.WriteString(fmt.Sprintf("End Date : %s\n", resp.ReplayConfig.GetEndTime().AsTime().Format(time.RFC3339))) + buff.WriteString(fmt.Sprintf("Replay Status : %s\n", resp.GetStatus())) + buff.WriteString(fmt.Sprintf("Total Runs : %d\n\n", len(resp.GetReplayRuns()))) + + if len(resp.ReplayConfig.GetJobConfig()) > 0 { + stringifyReplayConfig(buff, resp.ReplayConfig.GetJobConfig()) + buff.WriteString("\n") + } + + if len(resp.GetReplayRuns()) > 0 { + stringifyReplayRuns(buff, resp.GetReplayRuns()) + } + + return buff.String() +} + +func stringifyReplayConfig(buff *bytes.Buffer, jobConfig map[string]string) { + table := tablewriter.NewWriter(buff) + table.SetBorder(false) + table.SetHeader([]string{ + "config key", + "config value", + }) + table.SetAlignment(tablewriter.ALIGN_LEFT) + for k, v := range jobConfig { + table.Append([]string{k, v}) + } + table.Render() +} + +func stringifyReplayRuns(buff *bytes.Buffer, runs []*pb.ReplayRun) { + table := tablewriter.NewWriter(buff) + table.SetBorder(false) + table.SetHeader([]string{ + "scheduled at", + "status", + }) + table.SetAlignment(tablewriter.ALIGN_LEFT) + for _, run := range runs { + table.Append([]string{ + run.GetScheduledAt().AsTime().String(), + run.GetStatus(), + }) + } + table.Render() +} diff --git a/client/cmd/resource/apply.go b/client/cmd/resource/apply.go new file mode 100644 index 0000000000..9c45f353ad --- /dev/null +++ b/client/cmd/resource/apply.go @@ -0,0 +1,160 @@ +package resource + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/MakeNowJust/heredoc" + "github.com/raystack/salt/log" + "github.com/spf13/cobra" + + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/client/cmd/internal/progressbar" + "github.com/raystack/optimus/client/cmd/internal/survey" + "github.com/raystack/optimus/config" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" +) + +const ( + applyTimeout = time.Minute * 5 + successStatus = "success" +) + +type applyCommand struct { + logger log.Logger + connection connection.Connection + + configFilePath string + clientConfig *config.ClientConfig + + namespaceSurvey *survey.NamespaceSurvey + namespaceName string + projectName string + storeName string + + verbose bool + resourceNames []string +} + +// NewApplyCommand initializes command for applying resources from optimus to datastore +func NewApplyCommand() *cobra.Command { + l := logger.NewClientLogger() + apply := &applyCommand{ + logger: l, + namespaceSurvey: survey.NewNamespaceSurvey(l), + } + + cmd := &cobra.Command{ + Use: "apply", + Short: "Apply resources from optimus to datastore", + Long: heredoc.Doc(`Apply changes to destination datastore`), + Example: "optimus resource apply ", + Annotations: map[string]string{ + "group:core": "true", + }, + RunE: apply.RunE, + PreRunE: apply.PreRunE, + } + cmd.Flags().StringVarP(&apply.configFilePath, "config", "c", apply.configFilePath, "File path for client configuration") + cmd.Flags().StringSliceVarP(&apply.resourceNames, "resource-names", "R", nil, "Selected resources of optimus project") + cmd.Flags().BoolVarP(&apply.verbose, "verbose", "v", false, "Print details related to upload-all stages") + cmd.Flags().StringVarP(&apply.namespaceName, "namespace", "n", "", "Namespace name within project") + cmd.Flags().StringVarP(&apply.storeName, "datastore", "s", "bigquery", "Datastore type where the resource belongs") + return cmd +} + +func (a *applyCommand) PreRunE(_ *cobra.Command, _ []string) error { + var err error + a.clientConfig, err = config.LoadClientConfig(a.configFilePath) + if err != nil { + return err + } + + a.connection = connection.New(a.logger, a.clientConfig) + + return nil +} + +func (a *applyCommand) RunE(_ *cobra.Command, _ []string) error { + a.logger.Info("> Validating resource names") + if len(a.resourceNames) == 0 { + return errors.New("empty resource names") + } + + if a.projectName == "" { + a.projectName = a.clientConfig.Project.Name + } + + var namespace *config.Namespace + // use flag or ask namespace name + if a.namespaceName == "" { + var err error + namespace, err = a.namespaceSurvey.AskToSelectNamespace(a.clientConfig) + if err != nil { + return err + } + a.namespaceName = namespace.Name + } + + return a.apply() +} + +func (a *applyCommand) apply() error { + conn, err := a.connection.Create(a.clientConfig.Host) + if err != nil { + return err + } + defer conn.Close() + + apply := pb.NewResourceServiceClient(conn) + + spinner := progressbar.NewProgressBar() + spinner.Start("please wait...") + + applyRequest := pb.ApplyResourcesRequest{ + ProjectName: a.projectName, + NamespaceName: a.namespaceName, + DatastoreName: a.storeName, + ResourceNames: a.resourceNames, + } + + ctx, cancelFunc := context.WithTimeout(context.Background(), applyTimeout) + defer cancelFunc() + + responses, err := apply.ApplyResources(ctx, &applyRequest) + spinner.Stop() + if err != nil { + if errors.Is(err, context.DeadlineExceeded) { + a.logger.Error("Apply took too long, timing out") + } + return fmt.Errorf("failed to apply resourcse: %w", err) + } + + a.printApplyStatus(responses) + return nil +} + +func (a *applyCommand) printApplyStatus(responses *pb.ApplyResourcesResponse) { + a.logger.Info("Apply finished") + var successResources []string + for _, status := range responses.Statuses { + if status.Status == successStatus { + successResources = append(successResources, status.ResourceName) + } + } + if len(successResources) > 0 { + a.logger.Info("Resources with success") + for i, name := range successResources { + a.logger.Info("%d. %s", i+1, name) + } + } + + for _, resourceStatus := range responses.Statuses { + if resourceStatus.Status != successStatus { + a.logger.Error("Resource [%s] failed with: %s", resourceStatus.ResourceName, resourceStatus.Reason) + } + } +} diff --git a/client/cmd/resource/change_namespace.go b/client/cmd/resource/change_namespace.go new file mode 100644 index 0000000000..9b27af88bc --- /dev/null +++ b/client/cmd/resource/change_namespace.go @@ -0,0 +1,198 @@ +package resource + +import ( + "context" + "fmt" + "os" + "path/filepath" + "strings" + "time" + + "github.com/raystack/salt/log" + "github.com/spf13/afero" + "github.com/spf13/cobra" + + "github.com/raystack/optimus/client/cmd/internal" + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/client/local/specio" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/internal/errors" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" +) + +const ( + changeNamespaceTimeout = time.Minute * 1 +) + +type changeNamespaceCommand struct { + logger log.Logger + connection connection.Connection + + configFilePath string + clientConfig *config.ClientConfig + + project string + oldNamespace string + newNamespace string + dataStore string + host string +} + +// NewChangeNamespaceCommand initializes resource namespace change command +func NewChangeNamespaceCommand() *cobra.Command { + l := logger.NewClientLogger() + changeNamespace := &changeNamespaceCommand{ + logger: l, + } + cmd := &cobra.Command{ + Use: "change-namespace", + Short: "Change namespace of a resource", + Example: "optimus resource change-namespace --old-namespace --new-namespace ", + Args: cobra.MinimumNArgs(2), //nolint + PreRunE: changeNamespace.PreRunE, + RunE: changeNamespace.RunE, + PostRunE: changeNamespace.PostRunE, + } + // Config filepath flag + cmd.Flags().StringVarP(&changeNamespace.configFilePath, "config", "c", config.EmptyPath, "File path for client configuration") + internal.MarkFlagsRequired(cmd, []string{"old-namespace", "new-namespace"}) + changeNamespace.injectFlags(cmd) + + return cmd +} + +func (c *changeNamespaceCommand) injectFlags(cmd *cobra.Command) { + // Mandatory flags + cmd.Flags().StringVarP(&c.oldNamespace, "old-namespace", "o", "", "current namespace of the resource") + cmd.Flags().StringVarP(&c.newNamespace, "new-namespace", "n", "", "namespace to which the resource needs to be moved to") + + // Mandatory flags if config is not set + cmd.Flags().StringVarP(&c.project, "project-name", "p", "", "Name of the optimus project") + cmd.Flags().StringVar(&c.host, "host", "", "Optimus service endpoint url") +} + +func (c *changeNamespaceCommand) PreRunE(_ *cobra.Command, _ []string) error { + // Load mandatory config + conf, err := config.LoadClientConfig(c.configFilePath) + if err != nil { + return err + } + + c.clientConfig = conf + c.connection = connection.New(c.logger, c.clientConfig) + return err +} + +func (c *changeNamespaceCommand) RunE(_ *cobra.Command, args []string) error { + resourceFullName := args[0] + c.dataStore = args[1] + err := c.sendChangeNamespaceRequest(resourceFullName) + if err != nil { + return fmt.Errorf("namespace change request failed for resource %s: %w", resourceFullName, err) + } + c.logger.Info("successfully changed namespace and deployed new DAG on Scheduler") + return nil +} + +func (c *changeNamespaceCommand) sendChangeNamespaceRequest(resourceName string) error { + conn, err := c.connection.Create(c.host) + if err != nil { + return err + } + defer conn.Close() + + // fetch Instance by calling the optimus API + resourceRunServiceClient := pb.NewResourceServiceClient(conn) + request := &pb.ChangeResourceNamespaceRequest{ + ProjectName: c.project, + NamespaceName: c.oldNamespace, + DatastoreName: c.dataStore, + ResourceName: resourceName, + NewNamespaceName: c.newNamespace, + } + + ctx, cancelFunc := context.WithTimeout(context.Background(), changeNamespaceTimeout) + defer cancelFunc() + + _, err = resourceRunServiceClient.ChangeResourceNamespace(ctx, request) + return err +} + +func (c *changeNamespaceCommand) PostRunE(_ *cobra.Command, args []string) error { + c.logger.Info("\n[INFO] Moving resource in filesystem") + resourceName := args[0] + c.dataStore = args[1] + + oldNamespaceConfig, err := c.getResourceDatastoreConfig(c.oldNamespace, c.dataStore) + if err != nil { + c.logger.Error(fmt.Sprintf("[error] old namespace unregistered in filesystem, err: %s", err.Error())) + return nil + } + + resourceSpecReadWriter, err := specio.NewResourceSpecReadWriter(afero.NewOsFs()) + if err != nil { + c.logger.Error(fmt.Sprintf("[error] could not instantiate Spec Readed, err: %s", err.Error())) + return nil + } + + resourceSpec, err := resourceSpecReadWriter.ReadByName(oldNamespaceConfig.Path, resourceName) + if err != nil { + c.logger.Error(fmt.Sprintf("[error] unable to find resource in old namespace directory, err: %s", err.Error())) + return nil + } + + fs := afero.NewOsFs() + newNamespaceConfig, err := c.getResourceDatastoreConfig(c.newNamespace, c.dataStore) + if err != nil || newNamespaceConfig.Path == "" { + c.logger.Warn("[warn] new namespace not recognised for Resources") + c.logger.Warn("[info] run `optimus resource export` on the new namespace repo, to fetch the newly moved resource.") + + c.logger.Warn("[info] removing resource from old namespace") + err = fs.RemoveAll(resourceSpec.Path) + if err != nil { + c.logger.Error(fmt.Sprintf("[error] unable to remove resource from old namespace , err: %s", err.Error())) + c.logger.Warn("[info] consider deleting source files manually if they exist") + return nil + } + c.logger.Warn("[OK] removed resource spec from current namespace directory") + return nil + } + + newResourcePath := strings.Replace(resourceSpec.Path, oldNamespaceConfig.Path, newNamespaceConfig.Path, 1) + + c.logger.Info(fmt.Sprintf("\t* Old Path : '%s' \n\t* New Path : '%s' \n", resourceSpec.Path, newResourcePath)) + + c.logger.Info(fmt.Sprintf("[info] creating Resource directry: %s", newResourcePath)) + + err = fs.MkdirAll(filepath.Dir(newResourcePath), os.FileMode(0o755)) + if err != nil { + c.logger.Error(fmt.Sprintf("[error] unable to create path in the new namespace directory, err: %s", err.Error())) + c.logger.Warn("[warn] unable to move resource from old namespace") + c.logger.Warn("[info] consider moving source files manually") + return nil + } + + err = fs.Rename(resourceSpec.Path, newResourcePath) + if err != nil { + c.logger.Error(fmt.Sprintf("[warn] unable to move resource from old namespace, err: %s", err.Error())) + c.logger.Warn("[info] consider moving source files manually") + return nil + } + c.logger.Info("[OK] Resource moved successfully") + return nil +} + +func (c *changeNamespaceCommand) getResourceDatastoreConfig(namespaceName, datastoreName string) (*config.Datastore, error) { + for _, namespace := range c.clientConfig.Namespaces { + if namespace.Name == namespaceName { + for _, datastore := range namespace.Datastore { + if datastore.Type == datastoreName { + return &datastore, nil + } + } + } + } + return nil, errors.NotFound(resource.EntityResource, "not recognised in config") +} diff --git a/client/cmd/resource/create.go b/client/cmd/resource/create.go index 5c3b604278..af294114c2 100644 --- a/client/cmd/resource/create.go +++ b/client/cmd/resource/create.go @@ -4,15 +4,15 @@ import ( "fmt" "path/filepath" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/afero" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/client/cmd/internal/survey" - "github.com/odpf/optimus/client/local/model" - "github.com/odpf/optimus/client/local/specio" - "github.com/odpf/optimus/config" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/client/cmd/internal/survey" + "github.com/raystack/optimus/client/local/model" + "github.com/raystack/optimus/client/local/specio" + "github.com/raystack/optimus/config" ) type createCommand struct { diff --git a/client/cmd/resource/export.go b/client/cmd/resource/export.go index 3d9c6cef59..1cca048588 100644 --- a/client/cmd/resource/export.go +++ b/client/cmd/resource/export.go @@ -1,23 +1,24 @@ package resource import ( + "context" "errors" "fmt" "path" "strings" "time" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/afero" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/cmd/internal/connectivity" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/client/local" - "github.com/odpf/optimus/client/local/model" - "github.com/odpf/optimus/client/local/specio" - "github.com/odpf/optimus/config" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/client/local" + "github.com/raystack/optimus/client/local/model" + "github.com/raystack/optimus/client/local/specio" + "github.com/raystack/optimus/config" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) const ( @@ -26,7 +27,9 @@ const ( ) type exportCommand struct { - logger log.Logger + logger log.Logger + connection connection.Connection + writer local.SpecWriter[*model.ResourceSpec] configFilePath string @@ -87,6 +90,9 @@ func (e *exportCommand) PreRunE(_ *cobra.Command, _ []string) error { } else { e.host = cfg.Host } + + e.connection = connection.New(e.logger, cfg) + return nil } @@ -208,15 +214,18 @@ func (e *exportCommand) writeResources(projectName, namespaceName string, resour } func (e *exportCommand) fetchAllResources(projectName, namespaceName string) ([]*model.ResourceSpec, error) { - conn, err := connectivity.NewConnectivity(e.host, fetchResourceTimeout) + conn, err := e.connection.Create(e.host) if err != nil { return nil, err } defer conn.Close() - resourceServiceClient := pb.NewResourceServiceClient(conn.GetConnection()) + resourceServiceClient := pb.NewResourceServiceClient(conn) + + ctx, cancelFunc := context.WithTimeout(context.Background(), fetchResourceTimeout) + defer cancelFunc() - response, err := resourceServiceClient.ListResourceSpecification(conn.GetContext(), &pb.ListResourceSpecificationRequest{ + response, err := resourceServiceClient.ListResourceSpecification(ctx, &pb.ListResourceSpecificationRequest{ ProjectName: projectName, NamespaceName: namespaceName, DatastoreName: e.storeName, @@ -239,15 +248,18 @@ func (e *exportCommand) fetchAllResources(projectName, namespaceName string) ([] } func (e *exportCommand) fetchSpecificResource(projectName, namespaceName, resourceName string) (*model.ResourceSpec, error) { - conn, err := connectivity.NewConnectivity(e.host, fetchResourceTimeout) + conn, err := e.connection.Create(e.host) if err != nil { return nil, err } defer conn.Close() - resourceServiceClient := pb.NewResourceServiceClient(conn.GetConnection()) + resourceServiceClient := pb.NewResourceServiceClient(conn) - response, err := resourceServiceClient.ReadResource(conn.GetContext(), &pb.ReadResourceRequest{ + ctx, cancelFunc := context.WithTimeout(context.Background(), fetchResourceTimeout) + defer cancelFunc() + + response, err := resourceServiceClient.ReadResource(ctx, &pb.ReadResourceRequest{ ProjectName: projectName, NamespaceName: namespaceName, ResourceName: resourceName, @@ -266,15 +278,18 @@ func (e *exportCommand) fetchSpecificResource(projectName, namespaceName, resour } func (e *exportCommand) fetchNamespaceNames(projectName string) ([]string, error) { - conn, err := connectivity.NewConnectivity(e.host, fetchTenantTimeout) + conn, err := e.connection.Create(e.host) if err != nil { return nil, err } defer conn.Close() - namespaceServiceClient := pb.NewNamespaceServiceClient(conn.GetConnection()) + namespaceServiceClient := pb.NewNamespaceServiceClient(conn) + + ctx, cancelFunc := context.WithTimeout(context.Background(), fetchTenantTimeout) + defer cancelFunc() - response, err := namespaceServiceClient.ListProjectNamespaces(conn.GetContext(), &pb.ListProjectNamespacesRequest{ + response, err := namespaceServiceClient.ListProjectNamespaces(ctx, &pb.ListProjectNamespacesRequest{ ProjectName: projectName, }) if err != nil { @@ -289,15 +304,18 @@ func (e *exportCommand) fetchNamespaceNames(projectName string) ([]string, error } func (e *exportCommand) fetchProjectNames() ([]string, error) { - conn, err := connectivity.NewConnectivity(e.host, fetchTenantTimeout) + conn, err := e.connection.Create(e.host) if err != nil { return nil, err } defer conn.Close() - projectServiceClient := pb.NewProjectServiceClient(conn.GetConnection()) + projectServiceClient := pb.NewProjectServiceClient(conn) + + ctx, cancelFunc := context.WithTimeout(context.Background(), fetchTenantTimeout) + defer cancelFunc() - response, err := projectServiceClient.ListProjects(conn.GetContext(), &pb.ListProjectsRequest{}) + response, err := projectServiceClient.ListProjects(ctx, &pb.ListProjectsRequest{}) if err != nil { return nil, err } diff --git a/client/cmd/resource/resource.go b/client/cmd/resource/resource.go index 945e192fed..6daf1d0054 100644 --- a/client/cmd/resource/resource.go +++ b/client/cmd/resource/resource.go @@ -17,5 +17,7 @@ func NewResourceCommand() *cobra.Command { cmd.AddCommand(NewCreateCommand()) cmd.AddCommand(NewUploadAllCommand()) cmd.AddCommand(NewExportCommand()) + cmd.AddCommand(NewChangeNamespaceCommand()) + cmd.AddCommand(NewApplyCommand()) return cmd } diff --git a/client/cmd/resource/upload_all.go b/client/cmd/resource/upload_all.go index 0fcae22bfc..dd449e1cdc 100644 --- a/client/cmd/resource/upload_all.go +++ b/client/cmd/resource/upload_all.go @@ -9,15 +9,17 @@ import ( "time" "github.com/MakeNowJust/heredoc" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/afero" "github.com/spf13/cobra" - - "github.com/odpf/optimus/client/cmd/internal/connectivity" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/client/local/specio" - "github.com/odpf/optimus/config" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "google.golang.org/grpc" + + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/client/local/model" + "github.com/raystack/optimus/client/local/specio" + "github.com/raystack/optimus/config" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) const ( @@ -25,12 +27,16 @@ const ( ) type uploadAllCommand struct { - logger log.Logger + logger log.Logger + connection connection.Connection + clientConfig *config.ClientConfig selectedNamespaceNames []string verbose bool configFilePath string + + batchSize int } // NewUploadAllCommand initializes command for uploading all resources @@ -43,7 +49,7 @@ func NewUploadAllCommand() *cobra.Command { Use: "upload-all", Short: "Upload all current optimus resources to server", Long: heredoc.Doc(`Apply local changes to destination server which includes creating/updating resources`), - Example: "optimus resource upload-all [--verbose]", + Example: "optimus resource upload-all [--verbose | -b 1000]", Annotations: map[string]string{ "group:core": "true", }, @@ -53,6 +59,7 @@ func NewUploadAllCommand() *cobra.Command { cmd.Flags().StringVarP(&uploadAll.configFilePath, "config", "c", uploadAll.configFilePath, "File path for client configuration") cmd.Flags().StringSliceVarP(&uploadAll.selectedNamespaceNames, "namespace-names", "N", nil, "Selected namespaces of optimus project") cmd.Flags().BoolVarP(&uploadAll.verbose, "verbose", "v", false, "Print details related to upload-all stages") + cmd.Flags().IntVarP(&uploadAll.batchSize, "batch-size", "b", 0, "Number of resources to upload in a batch") return cmd } @@ -62,6 +69,8 @@ func (u *uploadAllCommand) PreRunE(_ *cobra.Command, _ []string) error { if err != nil { return err } + + u.connection = connection.New(u.logger, u.clientConfig) return nil } @@ -80,13 +89,16 @@ func (u *uploadAllCommand) RunE(_ *cobra.Command, _ []string) error { } func (u *uploadAllCommand) uploadAll(selectedNamespaces []*config.Namespace) error { - conn, err := connectivity.NewConnectivity(u.clientConfig.Host, uploadAllTimeout) + conn, err := u.connection.Create(u.clientConfig.Host) if err != nil { return err } defer conn.Close() - if err := u.uploadAllResources(conn, selectedNamespaces); err != nil { + ctx, cancelFunc := context.WithTimeout(context.Background(), uploadAllTimeout) + defer cancelFunc() + + if err := u.uploadAllResources(ctx, conn, selectedNamespaces); err != nil { return err } u.logger.Info("finished uploading resource specifications to server!\n") @@ -94,7 +106,7 @@ func (u *uploadAllCommand) uploadAll(selectedNamespaces []*config.Namespace) err return nil } -func (u *uploadAllCommand) uploadAllResources(conn *connectivity.Connectivity, selectedNamespaces []*config.Namespace) error { +func (u *uploadAllCommand) uploadAllResources(ctx context.Context, conn *grpc.ClientConn, selectedNamespaces []*config.Namespace) error { var namespaceNames []string for _, namespace := range selectedNamespaces { namespaceNames = append(namespaceNames, namespace.Name) @@ -102,7 +114,7 @@ func (u *uploadAllCommand) uploadAllResources(conn *connectivity.Connectivity, s u.logger.Info("> Uploading all resources for namespaces [%s]", strings.Join(namespaceNames, ",")) - stream, err := u.getResourceStreamClient(conn) + stream, err := u.getResourceStreamClient(ctx, conn) if err != nil { return err } @@ -135,34 +147,58 @@ func (u *uploadAllCommand) sendNamespaceResourceRequest(stream pb.ResourceServic datastoreSpecFs := CreateDataStoreSpecFs(namespace) for storeName, repoFS := range datastoreSpecFs { u.logger.Info("> Deploying %s resources for namespace [%s]", storeName, namespace.Name) - request, err := u.getResourceDeploymentRequest(namespace.Name, storeName, repoFS) + + resources, err := readResourceSpecs(repoFS) if err != nil { return fmt.Errorf("error getting resource specs for namespace [%s]: %w", namespace.Name, err) } - if err = stream.Send(request); err != nil { - return fmt.Errorf("resource upload for namespace [%s] failed: %w", namespace.Name, err) + resLength := len(resources) + size := resLength + if u.batchSize > 0 { + size = u.batchSize + } + + var errorReturned bool + for i := 0; i < resLength; i += size { + endIndex := i + size + if resLength < endIndex { + endIndex = resLength + } + + request, err := u.getResourceDeploymentRequest(namespace.Name, storeName, resources[i:endIndex]) + if err != nil { + u.logger.Error("Unable to get resource request, err: %s", err) + continue + } + + if err = stream.Send(request); err != nil { + errorReturned = true + u.logger.Error("Error: %s", err) + } + progressFn(len(request.GetResources())) + } + if errorReturned { + return fmt.Errorf("resource upload for namespace [%s] failed", namespace.Name) } - progressFn(len(request.GetResources())) } return nil } -func (u *uploadAllCommand) getResourceDeploymentRequest(namespaceName, storeName string, - repoFS afero.Fs, -) (*pb.DeployResourceSpecificationRequest, error) { +func readResourceSpecs(repoFS afero.Fs) ([]*model.ResourceSpec, error) { resourceSpecReadWriter, err := specio.NewResourceSpecReadWriter(repoFS) if err != nil { return nil, err } - resourceSpecs, err := resourceSpecReadWriter.ReadAll(".") - if err != nil { - return nil, err - } + return resourceSpecReadWriter.ReadAll(".") +} - resourceSpecsProto := make([]*pb.ResourceSpecification, len(resourceSpecs)) - for i, resourceSpec := range resourceSpecs { +func (u *uploadAllCommand) getResourceDeploymentRequest(namespaceName, storeName string, + resources []*model.ResourceSpec, +) (*pb.DeployResourceSpecificationRequest, error) { + resourceSpecsProto := make([]*pb.ResourceSpecification, len(resources)) + for i, resourceSpec := range resources { resourceSpecProto, err := resourceSpec.ToProto() if err != nil { return nil, err @@ -178,10 +214,10 @@ func (u *uploadAllCommand) getResourceDeploymentRequest(namespaceName, storeName }, nil } -func (u *uploadAllCommand) getResourceStreamClient(conn *connectivity.Connectivity) (pb.ResourceService_DeployResourceSpecificationClient, error) { - client := pb.NewResourceServiceClient(conn.GetConnection()) +func (u *uploadAllCommand) getResourceStreamClient(ctx context.Context, conn *grpc.ClientConn) (pb.ResourceService_DeployResourceSpecificationClient, error) { + client := pb.NewResourceServiceClient(conn) // TODO: create a new api for upload-all and remove deploy - stream, err := client.DeployResourceSpecification(conn.GetContext()) + stream, err := client.DeployResourceSpecification(ctx) if err != nil { if errors.Is(err, context.DeadlineExceeded) { u.logger.Error("Deployment of resources took too long, timing out") diff --git a/client/cmd/scheduler/upload_all.go b/client/cmd/scheduler/upload_all.go index 41db84e4f9..7f974f8f50 100644 --- a/client/cmd/scheduler/upload_all.go +++ b/client/cmd/scheduler/upload_all.go @@ -1,16 +1,17 @@ package scheduler import ( + "context" "time" "github.com/MakeNowJust/heredoc" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/cmd/internal/connectivity" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/config" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/config" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) const ( @@ -18,7 +19,9 @@ const ( ) type uploadCommand struct { - logger log.Logger + logger log.Logger + connection connection.Connection + clientConfig *config.ClientConfig configFilePath string @@ -52,24 +55,25 @@ func (u *uploadCommand) PreRunE(_ *cobra.Command, _ []string) error { return err } + u.connection = connection.New(u.logger, u.clientConfig) u.logger.Info("initialization finished!\n") return err } func (u *uploadCommand) RunE(_ *cobra.Command, _ []string) error { u.logger.Info("Uploading jobs for project " + u.clientConfig.Project.Name) - u.logger.Info("please wait...") _, err := u.sendUploadAllRequest(u.clientConfig.Project.Name) - u.logger.Info("Finished uploading to scheduler") if err != nil { - u.logger.Error("With %v", err.Error()) + u.logger.Error("Error: %v", err.Error()) + return err } + u.logger.Info("Triggered upload to scheduler, changes will be reflected in scheduler after a few minutes") return nil } func (u *uploadCommand) sendUploadAllRequest(projectName string) (*pb.UploadToSchedulerResponse, error) { - conn, err := connectivity.NewConnectivity(u.clientConfig.Host, uploadTimeout) + conn, err := u.connection.Create(u.clientConfig.Host) if err != nil { return nil, err } @@ -78,6 +82,10 @@ func (u *uploadCommand) sendUploadAllRequest(projectName string) (*pb.UploadToSc request := &pb.UploadToSchedulerRequest{ ProjectName: projectName, } - jobRunServiceClient := pb.NewJobRunServiceClient(conn.GetConnection()) - return jobRunServiceClient.UploadToScheduler(conn.GetContext(), request) + jobRunServiceClient := pb.NewJobRunServiceClient(conn) + + ctx, cancelFunc := context.WithTimeout(context.Background(), uploadTimeout) + defer cancelFunc() + + return jobRunServiceClient.UploadToScheduler(ctx, request) } diff --git a/client/cmd/secret/delete.go b/client/cmd/secret/delete.go index 60af62d7f6..fab1846158 100644 --- a/client/cmd/secret/delete.go +++ b/client/cmd/secret/delete.go @@ -5,19 +5,21 @@ import ( "errors" "fmt" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/cmd/internal" - "github.com/odpf/optimus/client/cmd/internal/connectivity" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/client/cmd/internal/progressbar" - "github.com/odpf/optimus/config" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/client/cmd/internal" + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/client/cmd/internal/progressbar" + "github.com/raystack/optimus/config" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) type deleteCommand struct { - logger log.Logger + logger log.Logger + connection connection.Connection + configFilePath string projectName string @@ -72,6 +74,9 @@ func (d *deleteCommand) PreRunE(cmd *cobra.Command, _ []string) error { if d.host == "" { d.host = conf.Host } + + d.connection = connection.New(d.logger, conf) + return nil } @@ -90,7 +95,7 @@ func (d *deleteCommand) RunE(_ *cobra.Command, args []string) error { } func (d *deleteCommand) deleteSecret(req *pb.DeleteSecretRequest) error { - conn, err := connectivity.NewConnectivity(d.host, secretTimeout) + conn, err := d.connection.Create(d.host) if err != nil { return err } @@ -98,9 +103,12 @@ func (d *deleteCommand) deleteSecret(req *pb.DeleteSecretRequest) error { spinner := progressbar.NewProgressBar() spinner.Start("please wait...") - secret := pb.NewSecretServiceClient(conn.GetConnection()) + secret := pb.NewSecretServiceClient(conn) + + ctx, cancelFunc := context.WithTimeout(context.Background(), secretTimeout) + defer cancelFunc() - _, err = secret.DeleteSecret(conn.GetContext(), req) + _, err = secret.DeleteSecret(ctx, req) spinner.Stop() if err != nil { if errors.Is(err, context.DeadlineExceeded) { diff --git a/client/cmd/secret/list.go b/client/cmd/secret/list.go index 5223c56d60..04d9342b0f 100644 --- a/client/cmd/secret/list.go +++ b/client/cmd/secret/list.go @@ -5,22 +5,25 @@ import ( "context" "errors" "fmt" + "sort" "time" - "github.com/odpf/salt/log" "github.com/olekukonko/tablewriter" + "github.com/raystack/salt/log" "github.com/spf13/cobra" - "github.com/odpf/optimus/client/cmd/internal" - "github.com/odpf/optimus/client/cmd/internal/connectivity" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/client/cmd/internal/progressbar" - "github.com/odpf/optimus/config" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/client/cmd/internal" + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/client/cmd/internal/progressbar" + "github.com/raystack/optimus/config" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) type listCommand struct { - logger log.Logger + logger log.Logger + connection connection.Connection + configFilePath string projectName string @@ -72,6 +75,9 @@ func (l *listCommand) PreRunE(cmd *cobra.Command, _ []string) error { if l.host == "" { l.host = conf.Host } + + l.connection = connection.New(l.logger, conf) + return nil } @@ -83,7 +89,7 @@ func (l *listCommand) RunE(_ *cobra.Command, _ []string) error { } func (l *listCommand) listSecret(req *pb.ListSecretsRequest) error { - conn, err := connectivity.NewConnectivity(l.host, secretTimeout) + conn, err := l.connection.Create(l.host) if err != nil { return err } @@ -91,9 +97,12 @@ func (l *listCommand) listSecret(req *pb.ListSecretsRequest) error { spinner := progressbar.NewProgressBar() spinner.Start("please wait...") - secret := pb.NewSecretServiceClient(conn.GetConnection()) + secret := pb.NewSecretServiceClient(conn) - listSecretsResponse, err := secret.ListSecrets(conn.GetContext(), req) + ctx, cancelFunc := context.WithTimeout(context.Background(), secretTimeout) + defer cancelFunc() + + listSecretsResponse, err := secret.ListSecrets(ctx, req) spinner.Stop() if err != nil { if errors.Is(err, context.DeadlineExceeded) { @@ -123,8 +132,12 @@ func (*listCommand) stringifyListOfSecrets(listSecretsResponse *pb.ListSecretsRe "Date", }) - table.SetAlignment(tablewriter.ALIGN_CENTER) - for _, secret := range listSecretsResponse.Secrets { + table.SetAlignment(tablewriter.ALIGN_LEFT) + secrets := listSecretsResponse.Secrets + sort.Slice(secrets, func(i, j int) bool { + return secrets[i].Name < secrets[j].Name + }) + for _, secret := range secrets { namespace := "*" if secret.Namespace != "" { namespace = secret.Namespace diff --git a/client/cmd/secret/set.go b/client/cmd/secret/set.go index 26da640bda..f05e046c33 100644 --- a/client/cmd/secret/set.go +++ b/client/cmd/secret/set.go @@ -5,22 +5,24 @@ import ( "errors" "fmt" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/spf13/cobra" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "github.com/odpf/optimus/client/cmd/internal" - "github.com/odpf/optimus/client/cmd/internal/connectivity" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/client/cmd/internal/progressbar" - "github.com/odpf/optimus/client/cmd/internal/survey" - "github.com/odpf/optimus/config" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/client/cmd/internal" + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/client/cmd/internal/progressbar" + "github.com/raystack/optimus/client/cmd/internal/survey" + "github.com/raystack/optimus/config" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) type setCommand struct { - logger log.Logger + logger log.Logger + connection connection.Connection + configFilePath string survey *survey.SecretSetSurvey @@ -92,6 +94,9 @@ func (s *setCommand) PreRunE(cmd *cobra.Command, _ []string) error { if s.host == "" { s.host = conf.Host } + + s.connection = connection.New(s.logger, conf) + return nil } @@ -146,7 +151,7 @@ func (s *setCommand) RunE(_ *cobra.Command, args []string) error { } func (s *setCommand) registerSecret(req *pb.RegisterSecretRequest) error { - conn, err := connectivity.NewConnectivity(s.host, secretTimeout) + conn, err := s.connection.Create(s.host) if err != nil { return err } @@ -154,9 +159,12 @@ func (s *setCommand) registerSecret(req *pb.RegisterSecretRequest) error { spinner := progressbar.NewProgressBar() spinner.Start("please wait...") - secret := pb.NewSecretServiceClient(conn.GetConnection()) + secret := pb.NewSecretServiceClient(conn) - _, err = secret.RegisterSecret(conn.GetContext(), req) + ctx, cancelFunc := context.WithTimeout(context.Background(), secretTimeout) + defer cancelFunc() + + _, err = secret.RegisterSecret(ctx, req) spinner.Stop() if err != nil { if errors.Is(err, context.DeadlineExceeded) { @@ -169,7 +177,7 @@ func (s *setCommand) registerSecret(req *pb.RegisterSecretRequest) error { } func (s *setCommand) updateSecret(req *pb.UpdateSecretRequest) error { - conn, err := connectivity.NewConnectivity(s.host, secretTimeout) + conn, err := s.connection.Create(s.host) if err != nil { return err } @@ -177,9 +185,12 @@ func (s *setCommand) updateSecret(req *pb.UpdateSecretRequest) error { spinner := progressbar.NewProgressBar() spinner.Start("please wait...") - secret := pb.NewSecretServiceClient(conn.GetConnection()) + secret := pb.NewSecretServiceClient(conn) + + ctx, cancelFunc := context.WithTimeout(context.Background(), secretTimeout) + defer cancelFunc() - _, err = secret.UpdateSecret(conn.GetContext(), req) + _, err = secret.UpdateSecret(ctx, req) spinner.Stop() if err != nil { if errors.Is(err, context.DeadlineExceeded) { diff --git a/client/cmd/version/version.go b/client/cmd/version/version.go index 434d45fc83..00ad4a517b 100644 --- a/client/cmd/version/version.go +++ b/client/cmd/version/version.go @@ -1,26 +1,31 @@ package version import ( + "context" "fmt" "time" - "github.com/odpf/salt/log" - "github.com/odpf/salt/version" + "github.com/raystack/salt/log" + "github.com/raystack/salt/version" "github.com/spf13/cobra" - - "github.com/odpf/optimus/client/cmd/internal" - "github.com/odpf/optimus/client/cmd/internal/connectivity" - "github.com/odpf/optimus/client/cmd/internal/logger" - "github.com/odpf/optimus/client/cmd/internal/progressbar" - "github.com/odpf/optimus/config" - "github.com/odpf/optimus/internal/models" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/raystack/optimus/client/cmd/internal" + "github.com/raystack/optimus/client/cmd/internal/connection" + "github.com/raystack/optimus/client/cmd/internal/logger" + "github.com/raystack/optimus/client/cmd/internal/progressbar" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/internal/models" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) const versionTimeout = time.Second * 2 type versionCommand struct { - logger log.Logger + logger log.Logger + connection connection.Connection + configFilePath string isWithServer bool @@ -71,6 +76,8 @@ func (v *versionCommand) PreRunE(cmd *cobra.Command, _ []string) error { } else if v.host == "" { v.host = conf.Host } + + v.connection = connection.New(v.logger, conf) } var err error @@ -92,7 +99,7 @@ func (v *versionCommand) RunE(_ *cobra.Command, _ []string) error { } // Print version update if new version is exist - githubRepo := "odpf/optimus" + githubRepo := "raystack/optimus" if updateNotice := version.UpdateNotice(config.BuildVersion, githubRepo); updateNotice != "" { v.logger.Info(updateNotice) } @@ -126,23 +133,29 @@ func (v *versionCommand) printAllPluginInfos() { } // getVersionRequest send a version request to service -func (*versionCommand) getVersionRequest(clientVer, host string) (ver string, err error) { - conn, err := connectivity.NewConnectivity(host, versionTimeout) +func (v *versionCommand) getVersionRequest(clientVer, host string) (ver string, err error) { + conn, err := v.connection.Create(host) if err != nil { return "", err } defer conn.Close() - runtime := pb.NewRuntimeServiceClient(conn.GetConnection()) + runtime := pb.NewRuntimeServiceClient(conn) spinner := progressbar.NewProgressBar() spinner.Start("please wait...") - versionResponse, err := runtime.Version(conn.GetContext(), &pb.VersionRequest{ + + ctx, cancelFunc := context.WithTimeout(context.Background(), versionTimeout) + defer cancelFunc() + + versionResponse, err := runtime.Version(ctx, &pb.VersionRequest{ Client: clientVer, }) if err != nil { + if status.Code(err) == codes.Unauthenticated { + return "", fmt.Errorf("please check if client_id belongs to this application") + } return "", fmt.Errorf("request failed for version: %w", err) } - time.Sleep(versionTimeout) spinner.Stop() return versionResponse.Server, nil } diff --git a/client/extension/default_asset_operator.go b/client/extension/default_asset_operator.go index c67c62b894..9825d10745 100644 --- a/client/extension/default_asset_operator.go +++ b/client/extension/default_asset_operator.go @@ -10,7 +10,7 @@ import ( "github.com/spf13/afero" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/model" ) // AssetOperatorFS is file system that will be used by operator. diff --git a/client/extension/default_asset_operator_test.go b/client/extension/default_asset_operator_test.go index b006d65c1d..e1e25a2250 100644 --- a/client/extension/default_asset_operator_test.go +++ b/client/extension/default_asset_operator_test.go @@ -9,7 +9,7 @@ import ( "github.com/spf13/afero" "github.com/stretchr/testify/suite" - "github.com/odpf/optimus/client/extension" + "github.com/raystack/optimus/client/extension" ) type DefaultAssetOperatorTestSuite struct { diff --git a/client/extension/default_manifester.go b/client/extension/default_manifester.go index d1ad8dd485..d853451b51 100644 --- a/client/extension/default_manifester.go +++ b/client/extension/default_manifester.go @@ -10,7 +10,7 @@ import ( "github.com/spf13/afero" "gopkg.in/yaml.v3" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/model" ) const manifestFileName = "manifest.yaml" diff --git a/client/extension/default_manifester_test.go b/client/extension/default_manifester_test.go index 8826d8387d..56f6598fa2 100644 --- a/client/extension/default_manifester_test.go +++ b/client/extension/default_manifester_test.go @@ -10,8 +10,8 @@ import ( "github.com/stretchr/testify/suite" "gopkg.in/yaml.v3" - "github.com/odpf/optimus/client/extension" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension" + "github.com/raystack/optimus/client/extension/model" ) const ( diff --git a/client/extension/extension.go b/client/extension/extension.go index 8bec9e65e3..982129680e 100644 --- a/client/extension/extension.go +++ b/client/extension/extension.go @@ -3,8 +3,8 @@ package extension import ( "github.com/spf13/afero" - "github.com/odpf/optimus/client/extension/internal" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/internal" + "github.com/raystack/optimus/client/extension/model" ) // CleanExtensionFS is file system that will be used when cleaning extension. diff --git a/client/extension/extension_test.go b/client/extension/extension_test.go index bd8393e045..a2b1de62b0 100644 --- a/client/extension/extension_test.go +++ b/client/extension/extension_test.go @@ -6,8 +6,8 @@ import ( "github.com/spf13/afero" "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/client/extension" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension" + "github.com/raystack/optimus/client/extension/model" ) func TestClean(t *testing.T) { diff --git a/client/extension/factory/factory.go b/client/extension/factory/factory.go index 397f41d425..9c204541a6 100644 --- a/client/extension/factory/factory.go +++ b/client/extension/factory/factory.go @@ -3,7 +3,7 @@ package factory import ( "fmt" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/model" ) // ParseRegistry is the registry for all parsers defined by each provider diff --git a/client/extension/factory/factory_test.go b/client/extension/factory/factory_test.go index d09bb9629b..6772ef3f8a 100644 --- a/client/extension/factory/factory_test.go +++ b/client/extension/factory/factory_test.go @@ -5,9 +5,9 @@ import ( "github.com/stretchr/testify/suite" - "github.com/odpf/optimus/client/extension/factory" - "github.com/odpf/optimus/client/extension/mock" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/factory" + "github.com/raystack/optimus/client/extension/mock" + "github.com/raystack/optimus/client/extension/model" ) type ClientFactoryTestSuite struct { diff --git a/client/extension/internal/activate.go b/client/extension/internal/activate.go index 1f9da48b34..74f93fc2b1 100644 --- a/client/extension/internal/activate.go +++ b/client/extension/internal/activate.go @@ -3,7 +3,7 @@ package internal import ( "fmt" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/model" ) // ActivateManager is an extension manater to manage tag activation process diff --git a/client/extension/internal/activate_test.go b/client/extension/internal/activate_test.go index 09b394b169..559faa1af2 100644 --- a/client/extension/internal/activate_test.go +++ b/client/extension/internal/activate_test.go @@ -8,9 +8,9 @@ import ( tMock "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" - "github.com/odpf/optimus/client/extension/internal" - "github.com/odpf/optimus/client/extension/mock" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/internal" + "github.com/raystack/optimus/client/extension/mock" + "github.com/raystack/optimus/client/extension/model" ) type ActivateManagerTestSuite struct { diff --git a/client/extension/internal/install.go b/client/extension/internal/install.go index cc926cace6..630ccd61e3 100644 --- a/client/extension/internal/install.go +++ b/client/extension/internal/install.go @@ -6,8 +6,8 @@ import ( "fmt" "time" - "github.com/odpf/optimus/client/extension/factory" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/factory" + "github.com/raystack/optimus/client/extension/model" ) type installResource struct { diff --git a/client/extension/internal/install_test.go b/client/extension/internal/install_test.go index bd3f52d845..636be74939 100644 --- a/client/extension/internal/install_test.go +++ b/client/extension/internal/install_test.go @@ -9,10 +9,10 @@ import ( tMock "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" - "github.com/odpf/optimus/client/extension/factory" - "github.com/odpf/optimus/client/extension/internal" - "github.com/odpf/optimus/client/extension/mock" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/factory" + "github.com/raystack/optimus/client/extension/internal" + "github.com/raystack/optimus/client/extension/mock" + "github.com/raystack/optimus/client/extension/model" ) type InstallManagerTestSuite struct { @@ -186,7 +186,7 @@ func (i *InstallManagerTestSuite) TestInstall() { provider := "testing" metadata := &model.Metadata{ ProviderName: provider, - OwnerName: "odpf", + OwnerName: "raystack", ProjectName: "optimus-extension-valor", } factory.ParseRegistry = []model.Parser{ @@ -208,7 +208,7 @@ func (i *InstallManagerTestSuite) TestInstall() { manifest := &model.Manifest{ RepositoryOwners: []*model.RepositoryOwner{ { - Name: "odpf", + Name: "raystack", Provider: provider, Projects: []*model.RepositoryProject{ { @@ -266,7 +266,7 @@ func (i *InstallManagerTestSuite) TestInstall() { manifest := &model.Manifest{ RepositoryOwners: []*model.RepositoryOwner{ { - Name: "odpf", + Name: "raystack", Provider: provider, Projects: []*model.RepositoryProject{ { diff --git a/client/extension/internal/internal.go b/client/extension/internal/internal.go index 48dff3d8ac..966aada49c 100644 --- a/client/extension/internal/internal.go +++ b/client/extension/internal/internal.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/model" ) func buildOwner(metadata *model.Metadata, project *model.RepositoryProject) *model.RepositoryOwner { diff --git a/client/extension/internal/rename.go b/client/extension/internal/rename.go index 23cc92abe8..373cc28318 100644 --- a/client/extension/internal/rename.go +++ b/client/extension/internal/rename.go @@ -3,7 +3,7 @@ package internal import ( "fmt" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/model" ) // RenameManager is an extension manater to manage command rename process diff --git a/client/extension/internal/rename_test.go b/client/extension/internal/rename_test.go index 62045cb885..faaf4339ab 100644 --- a/client/extension/internal/rename_test.go +++ b/client/extension/internal/rename_test.go @@ -8,9 +8,9 @@ import ( tMock "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" - "github.com/odpf/optimus/client/extension/internal" - "github.com/odpf/optimus/client/extension/mock" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/internal" + "github.com/raystack/optimus/client/extension/mock" + "github.com/raystack/optimus/client/extension/model" ) type RenameManagerTestSuite struct { diff --git a/client/extension/internal/run.go b/client/extension/internal/run.go index dad57f922d..ce2ba000ec 100644 --- a/client/extension/internal/run.go +++ b/client/extension/internal/run.go @@ -3,7 +3,7 @@ package internal import ( "fmt" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/model" ) type runResource struct { diff --git a/client/extension/internal/run_test.go b/client/extension/internal/run_test.go index aaf6727520..7e07d268c3 100644 --- a/client/extension/internal/run_test.go +++ b/client/extension/internal/run_test.go @@ -8,9 +8,9 @@ import ( tMock "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" - "github.com/odpf/optimus/client/extension/internal" - "github.com/odpf/optimus/client/extension/mock" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/internal" + "github.com/raystack/optimus/client/extension/mock" + "github.com/raystack/optimus/client/extension/model" ) type RunManagerTestSuite struct { diff --git a/client/extension/internal/uninstall.go b/client/extension/internal/uninstall.go index 650d030dac..d6a71690b8 100644 --- a/client/extension/internal/uninstall.go +++ b/client/extension/internal/uninstall.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/model" ) type uninstallResource struct { diff --git a/client/extension/internal/uninstall_test.go b/client/extension/internal/uninstall_test.go index 585c31f94f..d6def5180f 100644 --- a/client/extension/internal/uninstall_test.go +++ b/client/extension/internal/uninstall_test.go @@ -8,9 +8,9 @@ import ( tMock "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" - "github.com/odpf/optimus/client/extension/internal" - "github.com/odpf/optimus/client/extension/mock" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/internal" + "github.com/raystack/optimus/client/extension/mock" + "github.com/raystack/optimus/client/extension/model" ) type UninstallManagerTestSuite struct { diff --git a/client/extension/internal/upgrade.go b/client/extension/internal/upgrade.go index 0bbc4db86e..138422d6c8 100644 --- a/client/extension/internal/upgrade.go +++ b/client/extension/internal/upgrade.go @@ -5,8 +5,8 @@ import ( "fmt" "time" - "github.com/odpf/optimus/client/extension/factory" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/factory" + "github.com/raystack/optimus/client/extension/model" ) type upgradeResource struct { diff --git a/client/extension/internal/upgrade_test.go b/client/extension/internal/upgrade_test.go index 487c4260fb..e293030aed 100644 --- a/client/extension/internal/upgrade_test.go +++ b/client/extension/internal/upgrade_test.go @@ -9,10 +9,10 @@ import ( tMock "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" - "github.com/odpf/optimus/client/extension/factory" - "github.com/odpf/optimus/client/extension/internal" - "github.com/odpf/optimus/client/extension/mock" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/factory" + "github.com/raystack/optimus/client/extension/internal" + "github.com/raystack/optimus/client/extension/mock" + "github.com/raystack/optimus/client/extension/model" ) type UpgradeManagerTestSuite struct { @@ -230,7 +230,7 @@ func (u *UpgradeManagerTestSuite) TestUpgrade() { } release.Project = project3 owner1 := &model.RepositoryOwner{ - Name: "odpf", + Name: "raystack", Provider: provider, Projects: []*model.RepositoryProject{project1}, } diff --git a/client/extension/manager.go b/client/extension/manager.go index b1294c1fab..6e9726d269 100644 --- a/client/extension/manager.go +++ b/client/extension/manager.go @@ -3,8 +3,8 @@ package extension import ( "context" - "github.com/odpf/optimus/client/extension/internal" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/internal" + "github.com/raystack/optimus/client/extension/model" ) // Manager defines the extension management diff --git a/client/extension/manager_test.go b/client/extension/manager_test.go index 8e21e44e54..43f2074cca 100644 --- a/client/extension/manager_test.go +++ b/client/extension/manager_test.go @@ -8,10 +8,10 @@ import ( tMock "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" - "github.com/odpf/optimus/client/extension" - "github.com/odpf/optimus/client/extension/factory" - "github.com/odpf/optimus/client/extension/mock" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension" + "github.com/raystack/optimus/client/extension/factory" + "github.com/raystack/optimus/client/extension/mock" + "github.com/raystack/optimus/client/extension/model" ) type ManagerTestSuite struct { diff --git a/client/extension/mock/client.go b/client/extension/mock/client.go index 3f5996a8b6..adf0551d01 100644 --- a/client/extension/mock/client.go +++ b/client/extension/mock/client.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/model" ) // Client is an autogenerated mock type for the Client type diff --git a/client/extension/mock/manifester.go b/client/extension/mock/manifester.go index 101e1c289c..bc12210496 100644 --- a/client/extension/mock/manifester.go +++ b/client/extension/mock/manifester.go @@ -5,7 +5,7 @@ package mock import ( "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/model" ) // Manifester is an autogenerated mock type for the Manifester type diff --git a/client/extension/provider/github/client.go b/client/extension/provider/github/client.go index 5c5014671e..8fdd956a0c 100644 --- a/client/extension/provider/github/client.go +++ b/client/extension/provider/github/client.go @@ -9,8 +9,8 @@ import ( "runtime" "strings" - "github.com/odpf/optimus/client/extension/factory" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/factory" + "github.com/raystack/optimus/client/extension/model" ) const provider = "github" diff --git a/client/extension/provider/github/client_test.go b/client/extension/provider/github/client_test.go index 8a173af1b1..3f2797beb7 100644 --- a/client/extension/provider/github/client_test.go +++ b/client/extension/provider/github/client_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/suite" - "github.com/odpf/optimus/client/extension/provider/github" + "github.com/raystack/optimus/client/extension/provider/github" ) type ClientTestSuite struct { diff --git a/client/extension/provider/github/model.go b/client/extension/provider/github/model.go index 569d9551f5..15c53fb8f6 100644 --- a/client/extension/provider/github/model.go +++ b/client/extension/provider/github/model.go @@ -4,7 +4,7 @@ import ( "fmt" "regexp" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/model" ) const apiPrefix = "https://api.github.com/repos" diff --git a/client/extension/provider/github/parse.go b/client/extension/provider/github/parse.go index 907d5c3d6e..05dd05de79 100644 --- a/client/extension/provider/github/parse.go +++ b/client/extension/provider/github/parse.go @@ -7,8 +7,8 @@ import ( "regexp" "strings" - "github.com/odpf/optimus/client/extension/factory" - "github.com/odpf/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/factory" + "github.com/raystack/optimus/client/extension/model" ) // Parse parses remote path to get its metadata according to github convention diff --git a/client/extension/provider/github/parse_test.go b/client/extension/provider/github/parse_test.go index 0848e7ffa7..c5807f5cd6 100644 --- a/client/extension/provider/github/parse_test.go +++ b/client/extension/provider/github/parse_test.go @@ -7,8 +7,8 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/client/extension/model" - "github.com/odpf/optimus/client/extension/provider/github" + "github.com/raystack/optimus/client/extension/model" + "github.com/raystack/optimus/client/extension/provider/github" ) func TestParse(t *testing.T) { diff --git a/client/extension/provider/registry.go b/client/extension/provider/registry.go index 605cb419e9..65fafdbe6e 100644 --- a/client/extension/provider/registry.go +++ b/client/extension/provider/registry.go @@ -1,3 +1,3 @@ package provider -import _ "github.com/odpf/optimus/client/extension/provider/github" // init github client and parser +import _ "github.com/raystack/optimus/client/extension/provider/github" // init github client and parser diff --git a/client/local/internal/internal.go b/client/local/internal/internal.go index 8722186bb5..a4d733e09d 100644 --- a/client/local/internal/internal.go +++ b/client/local/internal/internal.go @@ -10,7 +10,7 @@ import ( "github.com/spf13/afero" "gopkg.in/yaml.v3" - "github.com/odpf/optimus/client/local" + "github.com/raystack/optimus/client/local" ) func DiscoverSpecDirPaths(specFS afero.Fs, rootSpecDir, referenceFileName string) ([]string, error) { diff --git a/client/local/model/job_spec.go b/client/local/model/job_spec.go index d95b88873c..a05f9e069e 100644 --- a/client/local/model/job_spec.go +++ b/client/local/model/job_spec.go @@ -5,8 +5,8 @@ import ( "google.golang.org/protobuf/types/known/durationpb" - "github.com/odpf/optimus/internal/utils" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/internal/utils" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) type JobSpec struct { @@ -33,7 +33,6 @@ type JobSpecSchedule struct { type JobSpecBehavior struct { DependsOnPast bool `yaml:"depends_on_past"` - Catchup bool `yaml:"catch_up"` Retry *JobSpecBehaviorRetry `yaml:"retry,omitempty"` Notify []JobSpecBehaviorNotifier `yaml:"notify,omitempty"` } @@ -109,7 +108,6 @@ func (j *JobSpec) ToProto() *pb.JobSpecification { EndDate: j.Schedule.EndDate, Interval: j.Schedule.Interval, DependsOnPast: j.Behavior.DependsOnPast, - CatchUp: j.Behavior.Catchup, TaskName: j.Task.Name, Config: j.getProtoJobConfigItems(), WindowSize: j.Task.Window.Size, @@ -260,7 +258,6 @@ func (j *JobSpec) MergeFrom(anotherJobSpec *JobSpec) { j.Behavior.Retry.Delay = getValue(j.Behavior.Retry.Delay, anotherJobSpec.Behavior.Retry.Delay) j.Behavior.Retry.Count = getValue(j.Behavior.Retry.Count, anotherJobSpec.Behavior.Retry.Count) j.Behavior.DependsOnPast = getValue(j.Behavior.DependsOnPast, anotherJobSpec.Behavior.DependsOnPast) - j.Behavior.Catchup = getValue(j.Behavior.Catchup, anotherJobSpec.Behavior.Catchup) for _, pNotify := range anotherJobSpec.Behavior.Notify { childNotifyIdx := -1 @@ -431,7 +428,7 @@ func ToJobSpec(protoSpec *pb.JobSpecification) *JobSpec { EndDate: protoSpec.EndDate, Interval: protoSpec.Interval, }, - Behavior: toJobSpecBehavior(protoSpec.Behavior, protoSpec.DependsOnPast, protoSpec.CatchUp), + Behavior: toJobSpecBehavior(protoSpec.Behavior, protoSpec.DependsOnPast), Task: JobSpecTask{ Name: protoSpec.TaskName, Config: configProtoToMap(protoSpec.Config), @@ -523,7 +520,7 @@ func toJobSpecHooks(protoHooks []*pb.JobSpecHook) []JobSpecHook { return hookSpecs } -func toJobSpecBehavior(protoBehavior *pb.JobSpecification_Behavior, dependsOnPast, catchUp bool) JobSpecBehavior { +func toJobSpecBehavior(protoBehavior *pb.JobSpecification_Behavior, dependsOnPast bool) JobSpecBehavior { var retry *JobSpecBehaviorRetry var notifiers []JobSpecBehaviorNotifier if protoBehavior != nil { @@ -547,7 +544,6 @@ func toJobSpecBehavior(protoBehavior *pb.JobSpecification_Behavior, dependsOnPas } return JobSpecBehavior{ DependsOnPast: dependsOnPast, - Catchup: catchUp, Retry: retry, Notify: notifiers, } diff --git a/client/local/model/job_spec_test.go b/client/local/model/job_spec_test.go index 5fed90ebdd..bdfa3803e4 100644 --- a/client/local/model/job_spec_test.go +++ b/client/local/model/job_spec_test.go @@ -7,8 +7,8 @@ import ( "github.com/stretchr/testify/suite" "google.golang.org/protobuf/types/known/durationpb" - "github.com/odpf/optimus/client/local/model" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/client/local/model" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) type JobSpecTestSuite struct { @@ -95,7 +95,6 @@ func (*JobSpecTestSuite) getCompleteJobSpec() model.JobSpec { }, Behavior: model.JobSpecBehavior{ DependsOnPast: true, - Catchup: true, Retry: &model.JobSpecBehaviorRetry{ Count: 10, Delay: 2 * time.Second, @@ -185,7 +184,6 @@ func (*JobSpecTestSuite) getCompleteJobSpecProto() *pb.JobSpecification { EndDate: "01-01-2050", Interval: "12 10 * * *", DependsOnPast: true, - CatchUp: true, Behavior: &pb.JobSpecification_Behavior{ Retry: &pb.JobSpecification_Behavior_Retry{ Count: 10, diff --git a/client/local/model/resource_spec.go b/client/local/model/resource_spec.go index 39ca07c253..3dce99701e 100644 --- a/client/local/model/resource_spec.go +++ b/client/local/model/resource_spec.go @@ -5,7 +5,7 @@ import ( "google.golang.org/protobuf/types/known/structpb" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) type ResourceSpec struct { @@ -14,6 +14,7 @@ type ResourceSpec struct { Type string `yaml:"type"` Labels map[string]string `yaml:"labels"` Spec map[string]interface{} `yaml:"spec"` + Path string `yaml:"-"` } func (r ResourceSpec) ToProto() (*pb.ResourceSpecification, error) { diff --git a/client/local/model/resource_spec_test.go b/client/local/model/resource_spec_test.go index 826e5f2f8a..eff8142a77 100644 --- a/client/local/model/resource_spec_test.go +++ b/client/local/model/resource_spec_test.go @@ -6,8 +6,8 @@ import ( "github.com/stretchr/testify/suite" "google.golang.org/protobuf/types/known/structpb" - "github.com/odpf/optimus/client/local/model" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/client/local/model" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) type ResourceSpecTestSuite struct { diff --git a/client/local/specio.go b/client/local/specio.go index c315dd1f7f..4b95e6671d 100644 --- a/client/local/specio.go +++ b/client/local/specio.go @@ -1,6 +1,6 @@ package local -import "github.com/odpf/optimus/client/local/model" +import "github.com/raystack/optimus/client/local/model" type ValidSpec interface { *model.JobSpec | *model.ResourceSpec diff --git a/client/local/specio/export_test.go b/client/local/specio/export_test.go index 38a14029b7..6bdf2b4cfc 100644 --- a/client/local/specio/export_test.go +++ b/client/local/specio/export_test.go @@ -3,8 +3,8 @@ package specio import ( "github.com/spf13/afero" - "github.com/odpf/optimus/client/local" - "github.com/odpf/optimus/client/local/model" + "github.com/raystack/optimus/client/local" + "github.com/raystack/optimus/client/local/model" ) func NewTestJobSpecReadWriter(specFS afero.Fs) local.SpecReadWriter[*model.JobSpec] { diff --git a/client/local/specio/job_spec_io.go b/client/local/specio/job_spec_io.go index 3bcc3278f6..4c4b4ad91e 100644 --- a/client/local/specio/job_spec_io.go +++ b/client/local/specio/job_spec_io.go @@ -11,9 +11,9 @@ import ( "github.com/spf13/afero" - "github.com/odpf/optimus/client/local" - "github.com/odpf/optimus/client/local/internal" - "github.com/odpf/optimus/client/local/model" + "github.com/raystack/optimus/client/local" + "github.com/raystack/optimus/client/local/internal" + "github.com/raystack/optimus/client/local/model" ) type jobSpecReadWriter struct { diff --git a/client/local/specio/job_spec_io_test.go b/client/local/specio/job_spec_io_test.go index 73460701b0..59bbf2943a 100644 --- a/client/local/specio/job_spec_io_test.go +++ b/client/local/specio/job_spec_io_test.go @@ -13,8 +13,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" - "github.com/odpf/optimus/client/local/model" - "github.com/odpf/optimus/client/local/specio" + "github.com/raystack/optimus/client/local/model" + "github.com/raystack/optimus/client/local/specio" ) type JobSpecReadWriterTestSuite struct { @@ -281,7 +281,6 @@ schedule: interval: "" behavior: depends_on_past: false - catch_up: false task: name: "" window: @@ -308,7 +307,6 @@ schedule: interval: 0 22 * * * behavior: depends_on_past: true - catch_up: false notify: - on: test channel: diff --git a/client/local/specio/resource_spec_io.go b/client/local/specio/resource_spec_io.go index a4aa77d475..d6b6a6d392 100644 --- a/client/local/specio/resource_spec_io.go +++ b/client/local/specio/resource_spec_io.go @@ -7,9 +7,9 @@ import ( "github.com/spf13/afero" - "github.com/odpf/optimus/client/local" - "github.com/odpf/optimus/client/local/internal" - "github.com/odpf/optimus/client/local/model" + "github.com/raystack/optimus/client/local" + "github.com/raystack/optimus/client/local/internal" + "github.com/raystack/optimus/client/local/model" ) type resourceSpecReadWriter struct { @@ -44,6 +44,7 @@ func (r resourceSpecReadWriter) ReadAll(rootDirPath string) ([]*model.ResourceSp if err != nil { return nil, fmt.Errorf("error reading spec under [%s]: %w", filePath, err) } + spec.Path = dirPath specs[i] = spec } return specs, nil diff --git a/client/local/specio/resource_spec_io_test.go b/client/local/specio/resource_spec_io_test.go index 1f2c0391e6..03bb3a9ebd 100644 --- a/client/local/specio/resource_spec_io_test.go +++ b/client/local/specio/resource_spec_io_test.go @@ -8,8 +8,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" - "github.com/odpf/optimus/client/local/model" - "github.com/odpf/optimus/client/local/specio" + "github.com/raystack/optimus/client/local/model" + "github.com/raystack/optimus/client/local/specio" ) type ResourceSpecReadWriterTestSuite struct { @@ -104,6 +104,7 @@ func (r *ResourceSpecReadWriterTestSuite) TestReadAll() { }, }, }, + Path: "namespace/resource/user", }, } @@ -230,6 +231,7 @@ func (r *ResourceSpecReadWriterTestSuite) TestReadByName() { }, }, }, + Path: "namespace/resource/user", } rootDirPath := "namespace" @@ -293,6 +295,7 @@ func (r *ResourceSpecReadWriterTestSuite) TestWrite() { }, }, }, + Path: "", } actualError := specReadWriter.Write(dirPath, spec) diff --git a/config.sample.yaml b/config.sample.yaml index 72d7714a63..928154ca86 100644 --- a/config.sample.yaml +++ b/config.sample.yaml @@ -4,7 +4,6 @@ version: 1 log: # debug, info, warning, error, fatal - default 'info' level: info - ######################################## # SERVER CONFIG ######################################## @@ -38,7 +37,6 @@ log: # # name of the registered scheduler, default: airflow2 # name: airflow2 - # application telemetry #telemetry: # @@ -63,4 +61,13 @@ log: # artifacts: # # refer : https://github.com/hashicorp/go-getter # - ../transformers/dist/transformers_0.1.0_macos_arm64.tar.gz -# - https://github.com/odpf/optimus/releases/download/v0.2.5/optimus_0.2.5_linux_arm64.tar.gz +# - https://github.com/raystack/optimus/releases/download/v0.2.5/optimus_0.2.5_linux_arm64.tar.gz + +# publisher: +# type: kafka +# buffer: 8 +# config: +# topic: optimus-events +# batch_interval_second: 1 +# broker_urls: +# - localhost:9092 diff --git a/config/config_client.go b/config/config_client.go index 9beacbff99..1903736f1d 100644 --- a/config/config_client.go +++ b/config/config_client.go @@ -11,6 +11,7 @@ type ClientConfig struct { Host string `mapstructure:"host"` // optimus server host Project Project `mapstructure:"project"` Namespaces []*Namespace `mapstructure:"namespaces"` + Auth Auth `mapstructure:"auth"` namespaceNameToNamespace map[string]*Namespace } @@ -30,6 +31,11 @@ type Project struct { Config map[string]string `mapstructure:"config"` } +type Auth struct { + ClientID string `mapstructure:"client_id"` + ClientSecret string `mapstructure:"client_secret"` +} + type Namespace struct { Name string `mapstructure:"name"` Config map[string]string `mapstructure:"config"` diff --git a/config/config_client_test.go b/config/config_client_test.go index 97b2fd51ae..9d8c8aa252 100644 --- a/config/config_client_test.go +++ b/config/config_client_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/suite" - "github.com/odpf/optimus/config" + "github.com/raystack/optimus/config" ) type ClientConfigTestSuite struct { diff --git a/config/config_server.go b/config/config_server.go index b50a34b80d..fb73cb4d20 100644 --- a/config/config_server.go +++ b/config/config_server.go @@ -11,6 +11,7 @@ type ServerConfig struct { ResourceManagers []ResourceManager `mapstructure:"resource_managers"` Plugin PluginConfig `mapstructure:"plugin"` Replay ReplayConfig `mapstructure:"replay"` + Publisher *Publisher `mapstructure:"publisher"` } type Serve struct { @@ -55,3 +56,15 @@ type PluginConfig struct { type ReplayConfig struct { ReplayTimeout time.Duration `mapstructure:"replay_timeout" default:"3h"` } + +type Publisher struct { + Type string `mapstructure:"type" default:"kafka"` + Buffer int `mapstructure:"buffer"` + Config interface{} `mapstructure:"config"` +} + +type PublisherKafkaConfig struct { + Topic string `mapstructure:"topic"` + BatchIntervalSecond int `mapstructure:"batch_interval_second"` + BrokerURLs []string `mapstructure:"broker_urls"` +} diff --git a/config/loader.go b/config/loader.go index 7c0253fd3f..2bbf46d92a 100644 --- a/config/loader.go +++ b/config/loader.go @@ -7,7 +7,7 @@ import ( "path/filepath" "strings" - "github.com/odpf/salt/config" + "github.com/raystack/salt/config" "github.com/spf13/afero" "github.com/spf13/viper" ) diff --git a/config/loader_test.go b/config/loader_test.go index 54f2d391f0..92db6c7888 100644 --- a/config/loader_test.go +++ b/config/loader_test.go @@ -9,11 +9,11 @@ import ( "testing" "time" - saltConfig "github.com/odpf/salt/config" + saltConfig "github.com/raystack/salt/config" "github.com/spf13/afero" "github.com/stretchr/testify/suite" - "github.com/odpf/optimus/config" + "github.com/raystack/optimus/config" ) const clientConfig = ` @@ -61,6 +61,14 @@ resource_managers: description: neighbor optimus config: host: external.optimus.io +publisher: + type: kafka + buffer: 8 + config: + topic: optimus_event_for_test + batch_interval_second: 5 + broker_urls: + - localhost:9092 ` type ConfigTestSuite struct { @@ -280,6 +288,16 @@ func (s *ConfigTestSuite) initExpectedServerConfig() { s.expectedServerConfig.Plugin = config.PluginConfig{} s.expectedServerConfig.Replay.ReplayTimeout = time.Hour * 3 + + s.expectedServerConfig.Publisher = &config.Publisher{ + Type: "kafka", + Buffer: 8, + Config: map[string]interface{}{ + "topic": "optimus_event_for_test", + "batch_interval_second": 5, + "broker_urls": []interface{}{"localhost:9092"}, + }, + } } func (*ConfigTestSuite) initServerConfigEnv() { diff --git a/config/validation_test.go b/config/validation_test.go index f7fadc0704..5218578bca 100644 --- a/config/validation_test.go +++ b/config/validation_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/suite" - "github.com/odpf/optimus/config" + "github.com/raystack/optimus/config" ) type ValidationTestSuite struct { diff --git a/core/event/event.go b/core/event/event.go new file mode 100644 index 0000000000..9a5b91f99d --- /dev/null +++ b/core/event/event.go @@ -0,0 +1,27 @@ +package event + +import ( + "time" + + "github.com/google/uuid" + + "github.com/raystack/optimus/internal/errors" +) + +const eventsEntity = "events" + +type Event struct { + ID uuid.UUID + OccurredAt time.Time +} + +func NewBaseEvent() (Event, error) { + id, err := uuid.NewRandom() + if err != nil { + return Event{}, errors.InternalError(eventsEntity, "not able to generate event uuid", err) + } + return Event{ + ID: id, + OccurredAt: time.Now(), + }, nil +} diff --git a/core/event/job.go b/core/event/job.go new file mode 100644 index 0000000000..18a3468f48 --- /dev/null +++ b/core/event/job.go @@ -0,0 +1,155 @@ +package event + +import ( + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" + + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/core/job/handler/v1beta1" + "github.com/raystack/optimus/core/tenant" + pbIntCore "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" + pbInt "github.com/raystack/optimus/protos/raystack/optimus/integration/v1beta1" +) + +type JobCreated struct { + Event + + Job *job.Job +} + +func NewJobCreatedEvent(job *job.Job) (*JobCreated, error) { + baseEvent, err := NewBaseEvent() + if err != nil { + return nil, err + } + return &JobCreated{ + Event: baseEvent, + Job: job, + }, nil +} + +func (j *JobCreated) Bytes() ([]byte, error) { + return jobEventToBytes(j.Event, j.Job, pbInt.OptimusChangeEvent_EVENT_TYPE_JOB_CREATE) +} + +type JobUpdated struct { + Event + + Job *job.Job +} + +func NewJobUpdateEvent(job *job.Job) (*JobUpdated, error) { + baseEvent, err := NewBaseEvent() + if err != nil { + return nil, err + } + return &JobUpdated{ + Event: baseEvent, + Job: job, + }, nil +} + +func (j *JobUpdated) Bytes() ([]byte, error) { + return jobEventToBytes(j.Event, j.Job, pbInt.OptimusChangeEvent_EVENT_TYPE_JOB_UPDATE) +} + +type JobDeleted struct { + Event + + JobName job.Name + JobTenant tenant.Tenant +} + +func NewJobDeleteEvent(tnnt tenant.Tenant, jobName job.Name) (*JobDeleted, error) { + baseEvent, err := NewBaseEvent() + if err != nil { + return nil, err + } + return &JobDeleted{ + Event: baseEvent, + JobName: jobName, + JobTenant: tnnt, + }, nil +} + +func (j *JobDeleted) Bytes() ([]byte, error) { + occurredAt := timestamppb.New(j.Event.OccurredAt) + optEvent := &pbInt.OptimusChangeEvent{ + EventId: j.Event.ID.String(), + OccurredAt: occurredAt, + ProjectName: j.JobTenant.ProjectName().String(), + NamespaceName: j.JobTenant.NamespaceName().String(), + EventType: pbInt.OptimusChangeEvent_EVENT_TYPE_JOB_DELETE, + Payload: &pbInt.OptimusChangeEvent_JobChange{ + JobChange: &pbInt.JobChangePayload{ + JobName: j.JobName.String(), + }, + }, + } + return proto.Marshal(optEvent) +} + +type JobStateChange struct { + Event + + JobName job.Name + JobTenant tenant.Tenant + State job.State +} + +func NewJobStateChangeEvent(tnnt tenant.Tenant, jobName job.Name, state job.State) (*JobStateChange, error) { + baseEvent, err := NewBaseEvent() + if err != nil { + return nil, err + } + return &JobStateChange{ + Event: baseEvent, + JobName: jobName, + JobTenant: tnnt, + State: state, + }, nil +} + +func (j *JobStateChange) Bytes() ([]byte, error) { + occurredAt := timestamppb.New(j.Event.OccurredAt) + var jobStateEnum pbIntCore.JobState + switch j.State { + case job.ENABLED: + jobStateEnum = pbIntCore.JobState_JOB_STATE_ENABLED + case job.DISABLED: + jobStateEnum = pbIntCore.JobState_JOB_STATE_DISABLED + } + optEvent := &pbInt.OptimusChangeEvent{ + EventId: j.Event.ID.String(), + OccurredAt: occurredAt, + ProjectName: j.JobTenant.ProjectName().String(), + NamespaceName: j.JobTenant.NamespaceName().String(), + EventType: pbInt.OptimusChangeEvent_EVENT_TYPE_JOB_STATE_CHANGE, + Payload: &pbInt.OptimusChangeEvent_JobStateChange{ + JobStateChange: &pbInt.JobStateChangePayload{ + JobName: j.JobName.String(), + State: jobStateEnum, + }, + }, + } + return proto.Marshal(optEvent) +} + +func jobEventToBytes(event Event, job *job.Job, eventType pbInt.OptimusChangeEvent_EventType) ([]byte, error) { + jobPb := v1beta1.ToJobProto(job) + occurredAt := timestamppb.New(event.OccurredAt) + optEvent := &pbInt.OptimusChangeEvent{ + EventId: event.ID.String(), + OccurredAt: occurredAt, + ProjectName: job.Tenant().ProjectName().String(), + NamespaceName: job.Tenant().NamespaceName().String(), + EventType: eventType, + Payload: &pbInt.OptimusChangeEvent_JobChange{ + JobChange: &pbInt.JobChangePayload{ + JobName: job.GetName(), + JobSpec: jobPb, + }, + }, + } + return proto.Marshal(optEvent) +} diff --git a/core/event/moderator/handler.go b/core/event/moderator/handler.go new file mode 100644 index 0000000000..27bdcb2b8f --- /dev/null +++ b/core/event/moderator/handler.go @@ -0,0 +1,47 @@ +package moderator + +import ( + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/raystack/salt/log" +) + +var eventQueueCounter = promauto.NewCounter(prometheus.CounterOpts{ + Name: "publisher_events_created_total", + Help: "Events created and to be sent to writer", +}) + +type Event interface { + Bytes() ([]byte, error) +} + +type Handler interface { + HandleEvent(e Event) +} + +type NoOpHandler struct{} + +func (NoOpHandler) HandleEvent(_ Event) {} + +type EventHandler struct { + messageChan chan<- []byte + logger log.Logger +} + +func NewEventHandler(messageChan chan<- []byte, logger log.Logger) *EventHandler { + return &EventHandler{ + messageChan: messageChan, + logger: logger, + } +} + +func (e EventHandler) HandleEvent(event Event) { + bytes, err := event.Bytes() + if err != nil { + e.logger.Error("error converting event to bytes: %v", err) + return + } + + go func() { e.messageChan <- bytes }() + eventQueueCounter.Inc() +} diff --git a/core/event/moderator/handler_test.go b/core/event/moderator/handler_test.go new file mode 100644 index 0000000000..f02c246abc --- /dev/null +++ b/core/event/moderator/handler_test.go @@ -0,0 +1,117 @@ +package moderator_test + +import ( + "errors" + "testing" + "time" + + "github.com/raystack/salt/log" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + + "github.com/raystack/optimus/core/event/moderator" +) + +func TestHandleEvent(t *testing.T) { + const buffer = 8 + logger := log.NewNoop() + timeout := 1 * time.Second + + t.Run("do not send message if there is error in extracting bytes of the event", func(t *testing.T) { + messageChan := make(chan []byte, buffer) + handler := moderator.NewEventHandler(messageChan, logger) + + event := NewEvent(t) + event.On("Bytes").Return(nil, errors.New("cannot get bytes representation")) + + handler.HandleEvent(event) + + timer := time.NewTimer(timeout) + var receivedEventBytes []byte + for { + done := false + select { + case bytes := <-messageChan: + receivedEventBytes = bytes + case <-timer.C: + done = true + } + if done { + break + } + } + assert.Nil(t, receivedEventBytes) + }) + + t.Run("send message if no error is found when extracting bytes from the event", func(t *testing.T) { + messageChan := make(chan []byte, buffer) + handler := moderator.NewEventHandler(messageChan, logger) + + sentEventBytes := []byte("message") + event := NewEvent(t) + event.On("Bytes").Return(sentEventBytes, nil) + + handler.HandleEvent(event) + + timer := time.NewTimer(timeout) + var receivedEventBytes []byte + for { + done := false + select { + case bytes := <-messageChan: + receivedEventBytes = bytes + case <-timer.C: + done = true + } + if done { + break + } + } + assert.EqualValues(t, sentEventBytes, receivedEventBytes) + }) +} + +type Event struct { + mock.Mock +} + +// Bytes provides a mock function with given fields: +func (_m *Event) Bytes() ([]byte, error) { + ret := _m.Called() + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func() ([]byte, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []byte); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewEvent interface { + mock.TestingT + Cleanup(func()) +} + +// NewEvent creates a new instance of Event. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewEvent(t mockConstructorTestingTNewEvent) *Event { + mock := &Event{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/event/moderator/worker.go b/core/event/moderator/worker.go new file mode 100644 index 0000000000..235664967b --- /dev/null +++ b/core/event/moderator/worker.go @@ -0,0 +1,84 @@ +package moderator + +import ( + "context" + "sync" + "time" + + "github.com/raystack/salt/log" +) + +type Writer interface { + Write(messages [][]byte) error + Close() error +} + +type Worker struct { + mu sync.Mutex + wg sync.WaitGroup + messageChan <-chan []byte + closeChan chan bool + + writer Writer + batchInterval time.Duration + + messages [][]byte + + logger log.Logger +} + +func NewWorker(messageChan <-chan []byte, writer Writer, batchInterval time.Duration, logger log.Logger) *Worker { + return &Worker{ + mu: sync.Mutex{}, + wg: sync.WaitGroup{}, + messageChan: messageChan, + closeChan: make(chan bool), + writer: writer, + batchInterval: batchInterval, + logger: logger, + } +} + +func (w *Worker) Run(ctx context.Context) { + w.wg.Add(1) + defer w.wg.Done() + + ticker := time.NewTicker(w.batchInterval) + for { + select { + case msg := <-w.messageChan: + w.mu.Lock() + w.messages = append(w.messages, msg) + w.mu.Unlock() + case <-ticker.C: + w.Flush() + case <-ctx.Done(): + return + case <-w.closeChan: + return + } + } +} + +func (w *Worker) Flush() { + w.mu.Lock() + defer w.mu.Unlock() + + if len(w.messages) == 0 { + return + } + + if err := w.writer.Write(w.messages); err != nil { + w.logger.Error("error writing message: %v", err) + } else { + w.messages = nil + } +} + +func (w *Worker) Close() error { + go func() { w.closeChan <- true }() + + w.wg.Wait() + w.Flush() + return w.writer.Close() +} diff --git a/core/event/resource.go b/core/event/resource.go new file mode 100644 index 0000000000..61c7553974 --- /dev/null +++ b/core/event/resource.go @@ -0,0 +1,90 @@ +package event + +import ( + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/timestamppb" + + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/internal/errors" + pbCore "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" + pbInt "github.com/raystack/optimus/protos/raystack/optimus/integration/v1beta1" +) + +type ResourceCreated struct { + Event + + Resource *resource.Resource +} + +func NewResourceCreatedEvent(rsc *resource.Resource) (*ResourceCreated, error) { + baseEvent, err := NewBaseEvent() + if err != nil { + return nil, err + } + return &ResourceCreated{ + Event: baseEvent, + Resource: rsc, + }, nil +} + +func (r ResourceCreated) Bytes() ([]byte, error) { + return resourceEventToBytes(r.Event, r.Resource, pbInt.OptimusChangeEvent_EVENT_TYPE_RESOURCE_CREATE) +} + +type ResourceUpdated struct { + Event + + Resource *resource.Resource +} + +func NewResourceUpdatedEvent(rsc *resource.Resource) (*ResourceUpdated, error) { + baseEvent, err := NewBaseEvent() + if err != nil { + return nil, err + } + return &ResourceUpdated{ + Event: baseEvent, + Resource: rsc, + }, nil +} + +func (r ResourceUpdated) Bytes() ([]byte, error) { + return resourceEventToBytes(r.Event, r.Resource, pbInt.OptimusChangeEvent_EVENT_TYPE_RESOURCE_UPDATE) +} + +func resourceEventToBytes(event Event, rsc *resource.Resource, eventType pbInt.OptimusChangeEvent_EventType) ([]byte, error) { + meta := rsc.Metadata() + if meta == nil { + return nil, errors.InvalidArgument(resource.EntityResource, "missing resource metadata") + } + + pbStruct, err := structpb.NewStruct(rsc.Spec()) + if err != nil { + return nil, errors.InvalidArgument(resource.EntityResource, "unable to convert spec to proto struct") + } + + resourcePb := &pbCore.ResourceSpecification{ + Version: meta.Version, + Name: rsc.FullName(), + Type: rsc.Kind(), + Spec: pbStruct, + Assets: nil, + Labels: meta.Labels, + } + occurredAt := timestamppb.New(event.OccurredAt) + optEvent := &pbInt.OptimusChangeEvent{ + EventId: event.ID.String(), + OccurredAt: occurredAt, + ProjectName: rsc.Tenant().ProjectName().String(), + NamespaceName: rsc.Tenant().NamespaceName().String(), + EventType: eventType, + Payload: &pbInt.OptimusChangeEvent_ResourceChange{ + ResourceChange: &pbInt.ResourceChangePayload{ + DatastoreName: rsc.Store().String(), + Resource: resourcePb, + }, + }, + } + return proto.Marshal(optEvent) +} diff --git a/core/event/scheduler.go b/core/event/scheduler.go new file mode 100644 index 0000000000..5ddfc9280e --- /dev/null +++ b/core/event/scheduler.go @@ -0,0 +1,111 @@ +package event + +import ( + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" + + "github.com/raystack/optimus/core/scheduler" + pbInt "github.com/raystack/optimus/protos/raystack/optimus/integration/v1beta1" +) + +type JobRunWaitUpstream struct { + Event + + JobRun *scheduler.JobRun +} + +func (j *JobRunWaitUpstream) Bytes() ([]byte, error) { + return proto.Marshal(toOptimusChangeEvent(j.JobRun, j.Event, pbInt.OptimusChangeEvent_EVENT_TYPE_JOB_WAIT_UPSTREAM)) +} + +type JobRunInProgress struct { + Event + + JobRun *scheduler.JobRun +} + +func (j *JobRunInProgress) Bytes() ([]byte, error) { + return proto.Marshal(toOptimusChangeEvent(j.JobRun, j.Event, pbInt.OptimusChangeEvent_EVENT_TYPE_JOB_IN_PROGRESS)) +} + +type JobRunSuccess struct { + Event + + JobRun *scheduler.JobRun +} + +func (j *JobRunSuccess) Bytes() ([]byte, error) { + return proto.Marshal(toOptimusChangeEvent(j.JobRun, j.Event, pbInt.OptimusChangeEvent_EVENT_TYPE_JOB_SUCCESS)) +} + +type JobRunFailed struct { + Event + + JobRun *scheduler.JobRun +} + +func (j *JobRunFailed) Bytes() ([]byte, error) { + return proto.Marshal(toOptimusChangeEvent(j.JobRun, j.Event, pbInt.OptimusChangeEvent_EVENT_TYPE_JOB_FAILURE)) +} + +func NewJobRunWaitUpstreamEvent(jobRun *scheduler.JobRun) (*JobRunWaitUpstream, error) { + baseEvent, err := NewBaseEvent() + if err != nil { + return nil, err + } + return &JobRunWaitUpstream{ + Event: baseEvent, + JobRun: jobRun, + }, nil +} + +func NewJobRunInProgressEvent(jobRun *scheduler.JobRun) (*JobRunInProgress, error) { + baseEvent, err := NewBaseEvent() + if err != nil { + return nil, err + } + return &JobRunInProgress{ + Event: baseEvent, + JobRun: jobRun, + }, nil +} + +func NewJobRunSuccessEvent(jobRun *scheduler.JobRun) (*JobRunSuccess, error) { + baseEvent, err := NewBaseEvent() + if err != nil { + return nil, err + } + return &JobRunSuccess{ + Event: baseEvent, + JobRun: jobRun, + }, nil +} + +func NewJobRunFailedEvent(jobRun *scheduler.JobRun) (*JobRunFailed, error) { + baseEvent, err := NewBaseEvent() + if err != nil { + return nil, err + } + return &JobRunFailed{ + Event: baseEvent, + JobRun: jobRun, + }, nil +} + +func toOptimusChangeEvent(j *scheduler.JobRun, e Event, eventType pbInt.OptimusChangeEvent_EventType) *pbInt.OptimusChangeEvent { + return &pbInt.OptimusChangeEvent{ + EventId: e.ID.String(), + OccurredAt: timestamppb.New(e.OccurredAt), + ProjectName: j.Tenant.ProjectName().String(), + NamespaceName: j.Tenant.NamespaceName().String(), + EventType: eventType, + Payload: &pbInt.OptimusChangeEvent_JobRun{ + JobRun: &pbInt.JobRunPayload{ + JobName: j.JobName.String(), + ScheduledAt: timestamppb.New(j.ScheduledAt), + JobRunId: j.ID.String(), + StartTime: timestamppb.New(j.StartTime), + }, + }, + } +} diff --git a/core/job/handler/v1beta1/job.go b/core/job/handler/v1beta1/job.go index 10731966d3..1e0b4c5746 100644 --- a/core/job/handler/v1beta1/job.go +++ b/core/job/handler/v1beta1/job.go @@ -7,34 +7,24 @@ import ( "strings" "time" - "github.com/odpf/salt/log" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/raystack/salt/log" "google.golang.org/protobuf/types/known/timestamppb" - "github.com/odpf/optimus/core/job" - "github.com/odpf/optimus/core/job/service/filter" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" - "github.com/odpf/optimus/internal/models" - "github.com/odpf/optimus/internal/writer" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" - "github.com/odpf/optimus/sdk/plugin" + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/core/job/service/filter" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/models" + "github.com/raystack/optimus/internal/telemetry" + "github.com/raystack/optimus/internal/writer" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" + "github.com/raystack/optimus/sdk/plugin" ) -var ( - jobReplaceAllDurationGauge = promauto.NewGauge(prometheus.GaugeOpts{ - Name: "jobs_replace_all_duration_in_seconds", - Help: "The duration of job replace all in seconds", - }) - jobRefreshDurationGauge = promauto.NewGauge(prometheus.GaugeOpts{ - Name: "jobs_refresh_duration_in_seconds", - Help: "The duration of job refresh in seconds", - }) - jobValidationDurationGauge = promauto.NewGauge(prometheus.GaugeOpts{ - Name: "jobs_validation_duration_in_seconds", - Help: "The duration of job validation in seconds", - }) +const ( + metricReplaceAllDuration = "job_replace_all_duration_seconds" + metricRefreshDuration = "job_refresh_duration_seconds" + metricValidationDuration = "job_validation_duration_seconds" ) type JobHandler struct { @@ -54,13 +44,16 @@ func NewJobHandler(jobService JobService, logger log.Logger) *JobHandler { type JobService interface { Add(ctx context.Context, jobTenant tenant.Tenant, jobs []*job.Spec) error Update(ctx context.Context, jobTenant tenant.Tenant, jobs []*job.Spec) error + SyncState(ctx context.Context, jobTenant tenant.Tenant, disabledJobNames, enabledJobNames []job.Name) error + UpdateState(ctx context.Context, jobTenant tenant.Tenant, jobNames []job.Name, jobState job.State, remark string) error + ChangeNamespace(ctx context.Context, jobSourceTenant, jobNewTenant tenant.Tenant, jobName job.Name) error Delete(ctx context.Context, jobTenant tenant.Tenant, jobName job.Name, cleanFlag, forceFlag bool) (affectedDownstream []job.FullName, err error) Get(ctx context.Context, jobTenant tenant.Tenant, jobName job.Name) (jobSpec *job.Job, err error) GetTaskInfo(ctx context.Context, task job.Task) (*plugin.Info, error) GetByFilter(ctx context.Context, filters ...filter.FilterOpt) (jobSpecs []*job.Job, err error) - ReplaceAll(ctx context.Context, jobTenant tenant.Tenant, jobs []*job.Spec, jobNamesWithValidationError []job.Name, logWriter writer.LogWriter) error + ReplaceAll(ctx context.Context, jobTenant tenant.Tenant, jobs []*job.Spec, jobNamesWithInvalidSpec []job.Name, logWriter writer.LogWriter) error Refresh(ctx context.Context, projectName tenant.ProjectName, namespaceNames, jobNames []string, logWriter writer.LogWriter) error - Validate(ctx context.Context, jobTenant tenant.Tenant, jobSpecs []*job.Spec, logWriter writer.LogWriter) error + Validate(ctx context.Context, jobTenant tenant.Tenant, jobSpecs []*job.Spec, jobNamesWithInvalidSpec []job.Name, logWriter writer.LogWriter) error GetJobBasicInfo(ctx context.Context, jobTenant tenant.Tenant, jobName job.Name, spec *job.Spec) (*job.Job, writer.BufferedLogger) GetUpstreamsToInspect(ctx context.Context, subjectJob *job.Job, localJob bool) ([]*job.Upstream, error) @@ -70,31 +63,34 @@ type JobService interface { func (jh *JobHandler) AddJobSpecifications(ctx context.Context, jobSpecRequest *pb.AddJobSpecificationsRequest) (*pb.AddJobSpecificationsResponse, error) { jobTenant, err := tenant.NewTenant(jobSpecRequest.ProjectName, jobSpecRequest.NamespaceName) if err != nil { + jh.l.Error("invalid tenant information request project [%s] namespace [%s]: %s", jobSpecRequest.GetProjectName(), jobSpecRequest.GetNamespaceName(), err) return nil, errors.GRPCErr(err, "failed to add job specifications") } me := errors.NewMultiError("add specs errors") - jobSpecs, _, err := fromJobProtos(jobSpecRequest.Specs) + jobSpecs, invalidSpecs, err := fromJobProtos(jobSpecRequest.Specs) if err != nil { errorMsg := fmt.Sprintf("failure when adapting job specifications: %s", err.Error()) jh.l.Error(errorMsg) me.Append(err) } + raiseJobEventMetric(jobTenant, job.MetricJobEventStateValidationFailed, len(invalidSpecs)) if len(jobSpecs) == 0 { + jh.l.Error("no jobs to be processed") me.Append(errors.NewError(errors.ErrFailedPrecond, job.EntityJob, "no jobs to be processed")) - return nil, errors.MultiToError(me) + return nil, me.ToErr() } if err = jh.jobService.Add(ctx, jobTenant, jobSpecs); err != nil { - jh.l.Error(fmt.Sprintf("failure found when adding job specifications: %s", err.Error())) + jh.l.Error("failure found when adding job specifications: %s", err) me.Append(err) } var responseLog string if len(me.Errors) > 0 { - responseLog = fmt.Sprintf("adding jobs finished with error: %s", errors.MultiToError(me)) + responseLog = fmt.Sprintf("adding jobs finished with error: %s", me.ToErr()) } else { responseLog = "jobs are successfully created" } @@ -129,6 +125,7 @@ func (jh *JobHandler) DeleteJobSpecification(ctx context.Context, deleteRequest msg := fmt.Sprintf("job %s has been deleted", jobName) if deleteRequest.Force && len(affectedDownstream) > 0 { msg = fmt.Sprintf("job %s has been forced deleted. these downstream will be affected: %s", jobName, job.FullNames(affectedDownstream).String()) + jh.l.Warn(msg) } return &pb.DeleteJobSpecificationResponse{ @@ -137,6 +134,43 @@ func (jh *JobHandler) DeleteJobSpecification(ctx context.Context, deleteRequest }, nil } +func (jh *JobHandler) ChangeJobNamespace(ctx context.Context, changeRequest *pb.ChangeJobNamespaceRequest) (*pb.ChangeJobNamespaceResponse, error) { + jobSourceTenant, err := tenant.NewTenant(changeRequest.ProjectName, changeRequest.NamespaceName) + if err != nil { + errorMsg := "failed to adapt source tenant when changing job namespace" + jh.l.Error(fmt.Sprintf("%s: %s", errorMsg, err.Error())) + return nil, errors.GRPCErr(err, errorMsg) + } + jobNewTenant, err := tenant.NewTenant(changeRequest.ProjectName, changeRequest.NewNamespaceName) + if err != nil { + errorMsg := "failed to adapt new tenant when changing job namespace" + jh.l.Error(fmt.Sprintf("%s: %s", errorMsg, err.Error())) + return nil, errors.GRPCErr(err, errorMsg) + } + + jobName, err := job.NameFrom(changeRequest.JobName) + if err != nil { + errorMsg := "failed to adapt job name when changing job specification" + jh.l.Error(fmt.Sprintf("%s: %s", errorMsg, err.Error())) + return nil, errors.GRPCErr(err, errorMsg) + } + + err = jh.jobService.ChangeNamespace(ctx, jobSourceTenant, jobNewTenant, jobName) + if err != nil { + errorMsg := "failed to change job namespace" + jh.l.Error(fmt.Sprintf("%s: %s", errorMsg, err.Error())) + return nil, errors.GRPCErr(err, errorMsg) + } + + telemetry.NewCounter("job_namespace_migrations_total", map[string]string{ + "project": jobSourceTenant.ProjectName().String(), + "namespace_source": jobSourceTenant.NamespaceName().String(), + "namespace_destination": jobNewTenant.NamespaceName().String(), + }).Inc() + + return &pb.ChangeJobNamespaceResponse{}, nil +} + func (jh *JobHandler) UpdateJobSpecifications(ctx context.Context, jobSpecRequest *pb.UpdateJobSpecificationsRequest) (*pb.UpdateJobSpecificationsResponse, error) { jobTenant, err := tenant.NewTenant(jobSpecRequest.ProjectName, jobSpecRequest.NamespaceName) if err != nil { @@ -146,16 +180,17 @@ func (jh *JobHandler) UpdateJobSpecifications(ctx context.Context, jobSpecReques } me := errors.NewMultiError("update specs errors") - jobSpecs, _, err := fromJobProtos(jobSpecRequest.Specs) + jobSpecs, invalidSpecs, err := fromJobProtos(jobSpecRequest.Specs) if err != nil { errorMsg := fmt.Sprintf("failure when adapting job specifications: %s", err.Error()) jh.l.Error(errorMsg) me.Append(err) } + raiseJobEventMetric(jobTenant, job.MetricJobEventStateValidationFailed, len(invalidSpecs)) if len(jobSpecs) == 0 { me.Append(errors.NewError(errors.ErrFailedPrecond, job.EntityJob, "no jobs to be processed")) - return nil, errors.MultiToError(me) + return nil, me.ToErr() } if err = jh.jobService.Update(ctx, jobTenant, jobSpecs); err != nil { @@ -165,7 +200,7 @@ func (jh *JobHandler) UpdateJobSpecifications(ctx context.Context, jobSpecReques var responseLog string if len(me.Errors) > 0 { - responseLog = fmt.Sprintf("update jobs finished with error: %s", errors.MultiToError(me)) + responseLog = fmt.Sprintf("update jobs finished with error: %s", me.ToErr()) } else { responseLog = "jobs are successfully updated" } @@ -178,10 +213,12 @@ func (jh *JobHandler) UpdateJobSpecifications(ctx context.Context, jobSpecReques func (jh *JobHandler) GetJobSpecification(ctx context.Context, req *pb.GetJobSpecificationRequest) (*pb.GetJobSpecificationResponse, error) { jobTenant, err := tenant.NewTenant(req.GetProjectName(), req.GetNamespaceName()) if err != nil { + jh.l.Error("invalid tenant information request project [%s] namespace [%s]: %s", req.GetProjectName(), req.GetNamespaceName(), err) return nil, err } jobName, err := job.NameFrom(req.GetJobName()) if err != nil { + jh.l.Error("error adapating job name [%s]: %s", req.GetJobName(), err) return nil, err } @@ -194,7 +231,7 @@ func (jh *JobHandler) GetJobSpecification(ctx context.Context, req *pb.GetJobSpe // TODO: return 404 if job is not found return &pb.GetJobSpecificationResponse{ - Spec: toJobProto(jobSpec), + Spec: ToJobProto(jobSpec), }, nil } @@ -211,7 +248,7 @@ func (jh *JobHandler) GetJobSpecifications(ctx context.Context, req *pb.GetJobSp jobSpecResponseProtos = append(jobSpecResponseProtos, &pb.JobSpecificationResponse{ ProjectName: jobSpec.Tenant().ProjectName().String(), NamespaceName: jobSpec.Tenant().NamespaceName().String(), - Job: toJobProto(jobSpec), + Job: ToJobProto(jobSpec), }) } @@ -229,7 +266,7 @@ func (jh *JobHandler) ListJobSpecification(ctx context.Context, req *pb.ListJobS jobSpecificationProtos := make([]*pb.JobSpecification, len(jobSpecs)) for i, jobSpec := range jobSpecs { - jobSpecificationProtos[i] = toJobProto(jobSpec) + jobSpecificationProtos[i] = ToJobProto(jobSpec) } // TODO: make a stream response @@ -238,10 +275,11 @@ func (jh *JobHandler) ListJobSpecification(ctx context.Context, req *pb.ListJobS }, merr } -func (*JobHandler) GetWindow(_ context.Context, req *pb.GetWindowRequest) (*pb.GetWindowResponse, error) { +func (jh *JobHandler) GetWindow(_ context.Context, req *pb.GetWindowRequest) (*pb.GetWindowResponse, error) { // TODO: the default version to be deprecated & made mandatory in future releases version := 1 if err := req.GetScheduledAt().CheckValid(); err != nil { + jh.l.Error("scheduled at is invalid: %s", err) return nil, fmt.Errorf("%w: failed to parse schedule time %s", err, req.GetScheduledAt()) } @@ -250,9 +288,11 @@ func (*JobHandler) GetWindow(_ context.Context, req *pb.GetWindowRequest) (*pb.G } window, err := models.NewWindow(version, req.GetTruncateTo(), req.GetOffset(), req.GetSize()) if err != nil { + jh.l.Error("error initializing window with version [%d]: %s", req.Version, err) return nil, err } if err := window.Validate(); err != nil { + jh.l.Error("error validating window: %s", err) return nil, err } @@ -285,11 +325,14 @@ func (jh *JobHandler) ReplaceAllJobSpecifications(stream pb.JobSpecificationServ if errors.Is(err, io.EOF) { break } + errMsg := fmt.Sprintf("error encountered when receiving stream request: %s", err) + jh.l.Error(errMsg) + responseWriter.Write(writer.LogLevelError, errMsg) return err } responseWriter.Write(writer.LogLevelInfo, fmt.Sprintf("[%s] received %d job specs", request.GetNamespaceName(), len(request.GetJobs()))) - jh.l.Info("replacing all job specifications for project [%s] namespace [%s]", request.GetProjectName(), request.GetNamespaceName()) + jh.l.Debug("replacing all job specifications for project [%s] namespace [%s]", request.GetProjectName(), request.GetNamespaceName()) startTime := time.Now() jobTenant, err := tenant.NewTenant(request.ProjectName, request.NamespaceName) @@ -302,7 +345,7 @@ func (jh *JobHandler) ReplaceAllJobSpecifications(stream pb.JobSpecificationServ continue } - jobSpecs, jobNamesWithValidationErrors, err := fromJobProtos(request.Jobs) + jobSpecs, jobNamesWithInvalidSpec, err := fromJobProtos(request.Jobs) if err != nil { errMsg := fmt.Sprintf("[%s] failed to adapt job specifications: %s", request.GetNamespaceName(), err.Error()) jh.l.Error(errMsg) @@ -311,7 +354,7 @@ func (jh *JobHandler) ReplaceAllJobSpecifications(stream pb.JobSpecificationServ errMessages = append(errMessages, errMsg) } - if err := jh.jobService.ReplaceAll(stream.Context(), jobTenant, jobSpecs, jobNamesWithValidationErrors, responseWriter); err != nil { + if err := jh.jobService.ReplaceAll(stream.Context(), jobTenant, jobSpecs, jobNamesWithInvalidSpec, responseWriter); err != nil { errMsg := fmt.Sprintf("[%s] replace all job specifications failure: %s", request.NamespaceName, err.Error()) jh.l.Error(errMsg) responseWriter.Write(writer.LogLevelError, errMsg) @@ -320,8 +363,11 @@ func (jh *JobHandler) ReplaceAllJobSpecifications(stream pb.JobSpecificationServ } processDuration := time.Since(startTime) - jh.l.Info("finished replacing all job specifications for project [%s] namespace [%s], took %s", request.GetProjectName(), request.GetNamespaceName(), processDuration) - jobReplaceAllDurationGauge.Set(processDuration.Seconds()) + jh.l.Debug("finished replacing all job specifications for project [%s] namespace [%s], took %s", request.GetProjectName(), request.GetNamespaceName(), processDuration) + telemetry.NewGauge(metricReplaceAllDuration, map[string]string{ + "project": jobTenant.ProjectName().String(), + "namespace": jobTenant.NamespaceName().String(), + }).Add(processDuration.Seconds()) } if len(errNamespaces) > 0 { errMessageSummary := strings.Join(errMessages, "\n") @@ -334,12 +380,13 @@ func (jh *JobHandler) ReplaceAllJobSpecifications(stream pb.JobSpecificationServ } func (jh *JobHandler) RefreshJobs(request *pb.RefreshJobsRequest, stream pb.JobSpecificationService_RefreshJobsServer) error { - jh.l.Info("refreshing jobs for project [%s]", request.GetProjectName()) startTime := time.Now() defer func() { processDuration := time.Since(startTime) - jobRefreshDurationGauge.Set(processDuration.Seconds()) - jh.l.Info("finished refreshing jobs for project [%s], took %s", request.GetProjectName(), processDuration) + telemetry.NewGauge(metricRefreshDuration, map[string]string{ + "project": request.ProjectName, + }).Add(processDuration.Seconds()) + jh.l.Debug("finished refreshing jobs for project [%s], took %s", request.GetProjectName(), processDuration) }() responseWriter := writer.NewRefreshJobResponseWriter(stream) @@ -359,57 +406,63 @@ func (jh *JobHandler) RefreshJobs(request *pb.RefreshJobsRequest, stream pb.JobS return err } responseWriter.Write(writer.LogLevelInfo, "jobs refreshed successfully") + return nil } func (jh *JobHandler) CheckJobSpecifications(req *pb.CheckJobSpecificationsRequest, stream pb.JobSpecificationService_CheckJobSpecificationsServer) error { - jh.l.Info("validating jobs for project [%s] namespace [%s]", req.GetProjectName(), req.GetNamespaceName()) startTime := time.Now() responseWriter := writer.NewCheckJobSpecificationResponseWriter(stream) jobTenant, err := tenant.NewTenant(req.ProjectName, req.NamespaceName) if err != nil { + jh.l.Error("invalid tenant information request project [%s] namespace [%s]: %s", req.GetProjectName(), req.GetNamespaceName(), err) return err } me := errors.NewMultiError("check / validate job spec errors") - jobSpecs, _, err := fromJobProtos(req.Jobs) + jobSpecs, jobNamesWithInvalidSpec, err := fromJobProtos(req.Jobs) if err != nil { - errorMsg := fmt.Sprintf("failure when adapting job specifications: %s", err.Error()) - jh.l.Error(errorMsg) + jh.l.Error("error when adapting job specifications: %s", err) me.Append(err) } - if err := jh.jobService.Validate(stream.Context(), jobTenant, jobSpecs, responseWriter); err != nil { + if err := jh.jobService.Validate(stream.Context(), jobTenant, jobSpecs, jobNamesWithInvalidSpec, responseWriter); err != nil { + jh.l.Error("error validating job: %s", err) me.Append(err) } processDuration := time.Since(startTime) - jobValidationDurationGauge.Set(processDuration.Seconds()) + telemetry.NewGauge(metricValidationDuration, map[string]string{ + "project": jobTenant.ProjectName().String(), + "namespace": jobTenant.NamespaceName().String(), + }).Add(processDuration.Seconds()) - jh.l.Info("finished validating jobs for project [%s] namespace [%s], took %s", req.GetProjectName(), req.GetNamespaceName(), processDuration) - - return errors.MultiToError(me) + return me.ToErr() } func (jh *JobHandler) GetJobTask(ctx context.Context, req *pb.GetJobTaskRequest) (*pb.GetJobTaskResponse, error) { jobTenant, err := tenant.NewTenant(req.GetProjectName(), req.GetNamespaceName()) if err != nil { + jh.l.Error("invalid tenant information request project [%s] namespace [%s]: %s", req.GetProjectName(), req.GetNamespaceName(), err) return nil, err } jobName, err := job.NameFrom(req.GetJobName()) if err != nil { + jh.l.Error("error adapting job name [%s]: %s", req.GetJobName(), err) return nil, err } jobResult, err := jh.jobService.Get(ctx, jobTenant, jobName) if err != nil { + jh.l.Error("error getting job: %s", err) return nil, err } taskInfo, err := jh.jobService.GetTaskInfo(ctx, jobResult.Spec().Task()) if err != nil { + jh.l.Error("error getting task info: %s", err) return nil, err } @@ -436,9 +489,81 @@ func (jh *JobHandler) GetJobTask(ctx context.Context, req *pb.GetJobTaskRequest) }, nil } +func (jh *JobHandler) UpdateJobsState(ctx context.Context, req *pb.UpdateJobsStateRequest) (*pb.UpdateJobsStateResponse, error) { + jobTenant, err := tenant.NewTenant(req.GetProjectName(), req.GetNamespaceName()) + if err != nil { + jh.l.Error("invalid tenant information request project [%s] namespace [%s]: %s", req.GetProjectName(), req.GetNamespaceName(), err) + return nil, err + } + jobState, err := job.StateFrom(req.GetState().String()) + if err != nil { + jh.l.Error("error adapting job state %s: %s", req.GetState().String(), err) + return nil, err + } + + remark := req.Remark + if len(remark) < 1 { + jh.l.Error("empty remark for changing %d jobs state of %s:%s to %s", len(req.GetJobNames()), jobState, jobTenant.ProjectName(), jobTenant.NamespaceName()) + return nil, errors.InvalidArgument(job.EntityJob, "can not update job state without a valid remark") + } + var jobNames []job.Name + for _, name := range req.GetJobNames() { + jobName, err := job.NameFrom(name) + if err != nil { + jh.l.Error("error adapting job name: '%s', err: %s", name, err.Error()) + return nil, err + } + jobNames = append(jobNames, jobName) + } + + err = jh.jobService.UpdateState(ctx, jobTenant, jobNames, jobState, remark) + if err != nil { + jh.l.Error("error updating job state", err.Error()) + return nil, err + } + + return &pb.UpdateJobsStateResponse{}, nil +} + +func (jh *JobHandler) SyncJobsState(ctx context.Context, req *pb.SyncJobsStateRequest) (*pb.SyncJobsStateResponse, error) { + jobTenant, err := tenant.NewTenant(req.GetProjectName(), req.GetNamespaceName()) + if err != nil { + jh.l.Error("invalid tenant information request project [%s] namespace [%s]: %s", req.GetProjectName(), req.GetNamespaceName(), err) + return nil, err + } + + var enabledJobNames, disabledJobNames []job.Name + for _, jobState := range req.GetJobStates() { + state, err := job.StateFrom(jobState.State.String()) + if err != nil { + jh.l.Error("error adapting job state %s: %s", jobState.State.String(), err) + return nil, err + } + jobName, err := job.NameFrom(jobState.JobName) + if err != nil { + jh.l.Error("error adapting job name: '%s', err: %s", jobState.JobName, err.Error()) + return nil, err + } + if state == job.DISABLED { + disabledJobNames = append(disabledJobNames, jobName) + } else { + enabledJobNames = append(enabledJobNames, jobName) + } + } + + err = jh.jobService.SyncState(ctx, jobTenant, disabledJobNames, enabledJobNames) + if err != nil { + jh.l.Error("error syncing job state for project: %s, namespace: %s, err: %s", jobTenant.ProjectName, jobTenant.NamespaceName(), err.Error()) + return nil, err + } + + return &pb.SyncJobsStateResponse{}, nil +} + func (jh *JobHandler) JobInspect(ctx context.Context, req *pb.JobInspectRequest) (*pb.JobInspectResponse, error) { jobTenant, err := tenant.NewTenant(req.GetProjectName(), req.GetNamespaceName()) if err != nil { + jh.l.Error("invalid tenant information request project [%s] namespace [%s]: %s", req.GetProjectName(), req.GetNamespaceName(), err) return nil, err } @@ -448,14 +573,14 @@ func (jh *JobHandler) JobInspect(ctx context.Context, req *pb.JobInspectRequest) if req.GetSpec() != nil { jobSpec, err = fromJobProto(req.GetSpec()) if err != nil { - errMsg := fmt.Sprintf("cannot adapt job specification %s: %s", req.Spec.Name, err.Error()) - jh.l.Error(errMsg) + jh.l.Error("cannot adapt job specification %s: %s", req.Spec.Name, err) return nil, err } localJob = true } else { jobName, err = job.NameFrom(req.JobName) if err != nil { + jh.l.Error("error adapting job name %s: %s", req.JobName, err) return nil, err } } @@ -473,12 +598,14 @@ func (jh *JobHandler) JobInspect(ctx context.Context, req *pb.JobInspectRequest) upstreamLogs := &writer.BufferedLogger{} upstreams, err := jh.jobService.GetUpstreamsToInspect(ctx, subjectJob, localJob) if err != nil { + jh.l.Error("error getting upstreams to inspect: %s", err) upstreamLogs.Write(writer.LogLevelError, fmt.Sprintf("unable to get upstream jobs: %v", err.Error())) } downstreamLogs := &writer.BufferedLogger{} downstreams, err := jh.jobService.GetDownstream(ctx, subjectJob, localJob) if err != nil { + jh.l.Error("error getting downstream: %s", err) downstreamLogs.Write(writer.LogLevelError, fmt.Sprintf("unable to get downstream jobs: %v", err.Error())) } @@ -494,3 +621,11 @@ func (jh *JobHandler) JobInspect(ctx context.Context, req *pb.JobInspectRequest) }, }, nil } + +func raiseJobEventMetric(jobTenant tenant.Tenant, state string, metricValue int) { + telemetry.NewCounter(job.MetricJobEvent, map[string]string{ + "project": jobTenant.ProjectName().String(), + "namespace": jobTenant.NamespaceName().String(), + "status": state, + }).Add(float64(metricValue)) +} diff --git a/core/job/handler/v1beta1/job_adapter.go b/core/job/handler/v1beta1/job_adapter.go index 0f119486e6..0333cdaa3d 100644 --- a/core/job/handler/v1beta1/job_adapter.go +++ b/core/job/handler/v1beta1/job_adapter.go @@ -5,14 +5,14 @@ import ( "google.golang.org/protobuf/types/known/durationpb" - "github.com/odpf/optimus/core/job" - "github.com/odpf/optimus/internal/errors" - "github.com/odpf/optimus/internal/models" - "github.com/odpf/optimus/internal/utils" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/models" + "github.com/raystack/optimus/internal/utils" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) -func toJobProto(jobEntity *job.Job) *pb.JobSpecification { +func ToJobProto(jobEntity *job.Job) *pb.JobSpecification { return &pb.JobSpecification{ Version: int32(jobEntity.Spec().Version()), Name: jobEntity.Spec().Name().String(), @@ -21,7 +21,6 @@ func toJobProto(jobEntity *job.Job) *pb.JobSpecification { EndDate: jobEntity.Spec().Schedule().EndDate().String(), Interval: jobEntity.Spec().Schedule().Interval(), DependsOnPast: jobEntity.Spec().Schedule().DependsOnPast(), - CatchUp: jobEntity.Spec().Schedule().CatchUp(), TaskName: jobEntity.Spec().Task().Name().String(), Config: fromConfig(jobEntity.Spec().Task().Config()), WindowSize: jobEntity.Spec().Window().GetSize(), @@ -57,7 +56,7 @@ func fromJobProtos(protoJobSpecs []*pb.JobSpecification) ([]*job.Spec, []job.Nam } jobSpecs = append(jobSpecs, jobSpec) } - return jobSpecs, jobNameWithValidationErrors, errors.MultiToError(me) + return jobSpecs, jobNameWithValidationErrors, me.ToErr() } func fromJobProto(js *pb.JobSpecification) (*job.Spec, error) { @@ -76,7 +75,6 @@ func fromJobProto(js *pb.JobSpecification) (*job.Spec, error) { } scheduleBuilder := job.NewScheduleBuilder(startDate). - WithCatchUp(js.CatchUp). WithDependsOnPast(js.DependsOnPast). WithInterval(js.Interval) @@ -413,7 +411,7 @@ func toBasicInfoSectionProto(jobDetail *job.Job, logMessages []*pb.Log) *pb.JobI return &pb.JobInspectResponse_BasicInfoSection{ Destination: jobDetail.Destination().String(), Source: sources, - Job: toJobProto(jobDetail), + Job: ToJobProto(jobDetail), Notice: logMessages, } } diff --git a/core/job/handler/v1beta1/job_test.go b/core/job/handler/v1beta1/job_test.go index 790ff21bb3..6ed8ba14c6 100644 --- a/core/job/handler/v1beta1/job_test.go +++ b/core/job/handler/v1beta1/job_test.go @@ -7,20 +7,20 @@ import ( "testing" "time" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "google.golang.org/grpc/metadata" "google.golang.org/protobuf/types/known/timestamppb" - "github.com/odpf/optimus/core/job" - "github.com/odpf/optimus/core/job/handler/v1beta1" - "github.com/odpf/optimus/core/job/service/filter" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/models" - "github.com/odpf/optimus/internal/writer" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" - "github.com/odpf/optimus/sdk/plugin" + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/core/job/handler/v1beta1" + "github.com/raystack/optimus/core/job/service/filter" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/models" + "github.com/raystack/optimus/internal/writer" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" + "github.com/raystack/optimus/sdk/plugin" ) func TestNewJobHandler(t *testing.T) { @@ -603,6 +603,145 @@ func TestNewJobHandler(t *testing.T) { assert.Contains(t, resp.Log, "error") }) }) + t.Run("ChangeJobNamespace", func(t *testing.T) { + newNamespaceName := "newNamespace" + + t.Run("fail if invalid params", func(t *testing.T) { + t.Run("invalid source namespace", func(t *testing.T) { + jobService := new(JobService) + defer jobService.AssertExpectations(t) + jobAName, _ := job.NameFrom("job-A") + request := &pb.ChangeJobNamespaceRequest{ + ProjectName: project.Name().String(), + NamespaceName: "", + JobName: jobAName.String(), + NewNamespaceName: newNamespaceName, + } + jobHandler := v1beta1.NewJobHandler(jobService, log) + _, err := jobHandler.ChangeJobNamespace(ctx, request) + assert.ErrorContains(t, err, "failed to adapt source tenant when changing job namespace") + }) + t.Run("invalid new namespace", func(t *testing.T) { + jobService := new(JobService) + defer jobService.AssertExpectations(t) + + jobAName, _ := job.NameFrom("job-A") + request := &pb.ChangeJobNamespaceRequest{ + ProjectName: project.Name().String(), + NamespaceName: namespace.Name().String(), + JobName: jobAName.String(), + NewNamespaceName: "", + } + jobHandler := v1beta1.NewJobHandler(jobService, log) + _, err := jobHandler.ChangeJobNamespace(ctx, request) + assert.ErrorContains(t, err, "failed to adapt new tenant when changing job namespace") + }) + t.Run("invalid job name", func(t *testing.T) { + jobService := new(JobService) + defer jobService.AssertExpectations(t) + + request := &pb.ChangeJobNamespaceRequest{ + ProjectName: project.Name().String(), + NamespaceName: namespace.Name().String(), + JobName: "", + NewNamespaceName: newNamespaceName, + } + jobHandler := v1beta1.NewJobHandler(jobService, log) + _, err := jobHandler.ChangeJobNamespace(ctx, request) + assert.ErrorContains(t, err, "failed to adapt job name when changing job specification") + }) + }) + + t.Run("Change job namespace successfully", func(t *testing.T) { + jobService := new(JobService) + + jobAName, _ := job.NameFrom("job-A") + request := &pb.ChangeJobNamespaceRequest{ + ProjectName: project.Name().String(), + NamespaceName: namespace.Name().String(), + JobName: jobAName.String(), + NewNamespaceName: newNamespaceName, + } + newTenant, _ := tenant.NewTenant(project.Name().String(), newNamespaceName) + jobService.On("ChangeNamespace", ctx, sampleTenant, newTenant, jobAName).Return(nil) + + jobHandler := v1beta1.NewJobHandler(jobService, log) + _, err := jobHandler.ChangeJobNamespace(ctx, request) + assert.NoError(t, err) + }) + t.Run("fail to Change job namespace", func(t *testing.T) { + jobService := new(JobService) + + jobAName, _ := job.NameFrom("job-A") + request := &pb.ChangeJobNamespaceRequest{ + ProjectName: project.Name().String(), + NamespaceName: namespace.Name().String(), + JobName: jobAName.String(), + NewNamespaceName: newNamespaceName, + } + newTenant, _ := tenant.NewTenant(project.Name().String(), newNamespaceName) + jobService.On("ChangeNamespace", ctx, sampleTenant, newTenant, jobAName).Return(errors.New("error in changing namespace")) + + jobHandler := v1beta1.NewJobHandler(jobService, log) + _, err := jobHandler.ChangeJobNamespace(ctx, request) + assert.ErrorContains(t, err, "error in changing namespace: failed to change job namespace") + }) + }) + t.Run("UpdateJobState", func(t *testing.T) { + updateRemark := "job state update remark" + jobAName, _ := job.NameFrom("job-A") + t.Run("fail if improper tenant info", func(t *testing.T) { + request := &pb.UpdateJobsStateRequest{ + ProjectName: project.Name().String(), + NamespaceName: "", + State: pb.JobState_JOB_STATE_ENABLED, + Remark: updateRemark, + JobNames: []string{jobAName.String()}, + } + + jobHandler := v1beta1.NewJobHandler(nil, log) + _, err := jobHandler.UpdateJobsState(ctx, request) + assert.ErrorContains(t, err, "namespace name is empty") + }) + t.Run("fail if improper job name", func(t *testing.T) { + request := &pb.UpdateJobsStateRequest{ + ProjectName: project.Name().String(), + NamespaceName: namespace.Name().String(), + State: pb.JobState_JOB_STATE_ENABLED, + Remark: updateRemark, + JobNames: []string{""}, + } + + jobHandler := v1beta1.NewJobHandler(nil, log) + _, err := jobHandler.UpdateJobsState(ctx, request) + assert.ErrorContains(t, err, "name is empty") + }) + t.Run("fail if State is improper", func(t *testing.T) { + request := &pb.UpdateJobsStateRequest{ + ProjectName: project.Name().String(), + NamespaceName: namespace.Name().String(), + State: pb.JobState_JOB_STATE_UNSPECIFIED, + Remark: updateRemark, + JobNames: []string{jobAName.String()}, + } + jobHandler := v1beta1.NewJobHandler(nil, log) + _, err := jobHandler.UpdateJobsState(ctx, request) + assert.ErrorContains(t, err, "invalid state") + }) + t.Run("fail if remark is empty", func(t *testing.T) { + request := &pb.UpdateJobsStateRequest{ + ProjectName: project.Name().String(), + NamespaceName: namespace.Name().String(), + JobNames: []string{jobAName.String()}, + State: pb.JobState_JOB_STATE_ENABLED, + Remark: "", + } + + jobHandler := v1beta1.NewJobHandler(nil, log) + _, err := jobHandler.UpdateJobsState(ctx, request) + assert.ErrorContains(t, err, "can not update job state without a valid remark") + }) + }) t.Run("DeleteJobSpecification", func(t *testing.T) { t.Run("deletes job successfully", func(t *testing.T) { jobService := new(JobService) @@ -699,7 +838,7 @@ func TestNewJobHandler(t *testing.T) { req := &pb.GetWindowRequest{ ScheduledAt: nil, } - jobHandler := v1beta1.NewJobHandler(nil, nil) + jobHandler := v1beta1.NewJobHandler(nil, log) resp, err := jobHandler.GetWindow(ctx, req) assert.Error(t, err) @@ -710,7 +849,7 @@ func TestNewJobHandler(t *testing.T) { Version: 3, ScheduledAt: timestamppb.New(time.Date(2022, 11, 18, 13, 0, 0, 0, time.UTC)), } - jobHandler := v1beta1.NewJobHandler(nil, nil) + jobHandler := v1beta1.NewJobHandler(nil, log) resp, err := jobHandler.GetWindow(ctx, req) assert.Error(t, err) @@ -722,7 +861,7 @@ func TestNewJobHandler(t *testing.T) { ScheduledAt: timestamppb.New(time.Date(2022, 11, 18, 13, 0, 0, 0, time.UTC)), Size: "1", } - jobHandler := v1beta1.NewJobHandler(nil, nil) + jobHandler := v1beta1.NewJobHandler(nil, log) resp, err := jobHandler.GetWindow(ctx, req) assert.Error(t, err) @@ -736,7 +875,7 @@ func TestNewJobHandler(t *testing.T) { Offset: "0", TruncateTo: "d", } - jobHandler := v1beta1.NewJobHandler(nil, nil) + jobHandler := v1beta1.NewJobHandler(nil, log) resp, err := jobHandler.GetWindow(ctx, req) assert.NoError(t, err) @@ -749,7 +888,7 @@ func TestNewJobHandler(t *testing.T) { Offset: "0", TruncateTo: "d", } - jobHandler := v1beta1.NewJobHandler(nil, nil) + jobHandler := v1beta1.NewJobHandler(nil, log) resp, err := jobHandler.GetWindow(ctx, req) assert.NoError(t, err) @@ -757,7 +896,7 @@ func TestNewJobHandler(t *testing.T) { }) }) t.Run("ReplaceAllJobSpecifications", func(t *testing.T) { - var jobNamesWithValidationError []job.Name + var jobNamesWithInvalidSpec []job.Name t.Run("replaces all job specifications of a tenant", func(t *testing.T) { jobService := new(JobService) @@ -800,7 +939,7 @@ func TestNewJobHandler(t *testing.T) { stream.On("Recv").Return(request, nil).Once() stream.On("Recv").Return(nil, io.EOF).Once() - jobService.On("ReplaceAll", ctx, sampleTenant, mock.Anything, jobNamesWithValidationError, mock.Anything).Return(nil) + jobService.On("ReplaceAll", ctx, sampleTenant, mock.Anything, jobNamesWithInvalidSpec, mock.Anything).Return(nil) stream.On("Send", mock.AnythingOfType("*optimus.ReplaceAllJobSpecificationsResponse")).Return(nil).Twice() @@ -845,8 +984,8 @@ func TestNewJobHandler(t *testing.T) { stream.On("Recv").Return(request2, nil).Once() stream.On("Recv").Return(nil, io.EOF).Once() - jobService.On("ReplaceAll", ctx, sampleTenant, mock.Anything, jobNamesWithValidationError, mock.Anything).Return(nil) - jobService.On("ReplaceAll", ctx, otherTenant, mock.Anything, jobNamesWithValidationError, mock.Anything).Return(nil) + jobService.On("ReplaceAll", ctx, sampleTenant, mock.Anything, jobNamesWithInvalidSpec, mock.Anything).Return(nil) + jobService.On("ReplaceAll", ctx, otherTenant, mock.Anything, jobNamesWithInvalidSpec, mock.Anything).Return(nil) stream.On("Send", mock.AnythingOfType("*optimus.ReplaceAllJobSpecificationsResponse")).Return(nil).Twice() @@ -939,7 +1078,7 @@ func TestNewJobHandler(t *testing.T) { stream.On("Recv").Return(request2, nil).Once() stream.On("Recv").Return(nil, io.EOF).Once() - jobService.On("ReplaceAll", ctx, sampleTenant, mock.Anything, jobNamesWithValidationError, mock.Anything).Return(nil) + jobService.On("ReplaceAll", ctx, sampleTenant, mock.Anything, jobNamesWithInvalidSpec, mock.Anything).Return(nil) stream.On("Send", mock.AnythingOfType("*optimus.ReplaceAllJobSpecificationsResponse")).Return(nil).Times(4) @@ -988,7 +1127,7 @@ func TestNewJobHandler(t *testing.T) { stream.On("Recv").Return(request, nil).Once() stream.On("Recv").Return(nil, io.EOF).Once() - jobService.On("ReplaceAll", ctx, sampleTenant, mock.Anything, jobNamesWithValidationError, mock.Anything).Return(errors.New("internal error")) + jobService.On("ReplaceAll", ctx, sampleTenant, mock.Anything, jobNamesWithInvalidSpec, mock.Anything).Return(errors.New("internal error")) stream.On("Send", mock.AnythingOfType("*optimus.ReplaceAllJobSpecificationsResponse")).Return(nil).Times(3) @@ -1355,7 +1494,6 @@ func TestNewJobHandler(t *testing.T) { EndDate: specA.Schedule().EndDate().String(), Interval: specA.Schedule().Interval(), DependsOnPast: specA.Schedule().DependsOnPast(), - CatchUp: specA.Schedule().CatchUp(), TaskName: specA.Task().Name().String(), WindowSize: specA.Window().GetSize(), WindowOffset: specA.Window().GetOffset(), @@ -1495,7 +1633,6 @@ func TestNewJobHandler(t *testing.T) { EndDate: specA.Schedule().EndDate().String(), Interval: specA.Schedule().Interval(), DependsOnPast: specA.Schedule().DependsOnPast(), - CatchUp: specA.Schedule().CatchUp(), TaskName: specA.Task().Name().String(), WindowSize: specA.Window().GetSize(), WindowOffset: specA.Window().GetOffset(), @@ -1641,7 +1778,6 @@ func TestNewJobHandler(t *testing.T) { EndDate: specA.Schedule().EndDate().String(), Interval: specA.Schedule().Interval(), DependsOnPast: specA.Schedule().DependsOnPast(), - CatchUp: specA.Schedule().CatchUp(), TaskName: specA.Task().Name().String(), WindowSize: specA.Window().GetSize(), WindowOffset: specA.Window().GetOffset(), @@ -1688,7 +1824,7 @@ func TestNewJobHandler(t *testing.T) { }, } - handler := v1beta1.NewJobHandler(jobService, nil) + handler := v1beta1.NewJobHandler(jobService, log) result, err := handler.JobInspect(ctx, req) assert.NoError(t, err) assert.Equal(t, resp, result) @@ -1724,7 +1860,7 @@ func TestNewJobHandler(t *testing.T) { req := &pb.GetJobTaskRequest{} - handler := v1beta1.NewJobHandler(jobService, nil) + handler := v1beta1.NewJobHandler(jobService, log) resp, err := handler.GetJobTask(ctx, req) assert.Error(t, err) assert.Nil(t, resp) @@ -1739,7 +1875,7 @@ func TestNewJobHandler(t *testing.T) { NamespaceName: sampleTenant.NamespaceName().String(), } - handler := v1beta1.NewJobHandler(jobService, nil) + handler := v1beta1.NewJobHandler(jobService, log) resp, err := handler.GetJobTask(ctx, req) assert.Error(t, err) assert.Nil(t, resp) @@ -1756,7 +1892,7 @@ func TestNewJobHandler(t *testing.T) { } jobService.On("Get", ctx, sampleTenant, job.Name("job-A")).Return(nil, errors.New("error encountered")) - handler := v1beta1.NewJobHandler(jobService, nil) + handler := v1beta1.NewJobHandler(jobService, log) resp, err := handler.GetJobTask(ctx, req) assert.Error(t, err) assert.Nil(t, resp) @@ -1776,7 +1912,7 @@ func TestNewJobHandler(t *testing.T) { jobService.On("Get", ctx, sampleTenant, jobA.Spec().Name()).Return(jobA, nil) jobService.On("GetTaskInfo", ctx, jobA.Spec().Task()).Return(nil, errors.New("error encountered")) - handler := v1beta1.NewJobHandler(jobService, nil) + handler := v1beta1.NewJobHandler(jobService, log) resp, err := handler.GetJobTask(ctx, req) assert.Error(t, err) assert.Nil(t, resp) @@ -1797,11 +1933,11 @@ func TestNewJobHandler(t *testing.T) { taskInfo := &plugin.Info{ Name: "bq2bq", Description: "task info desc", - Image: "odpf/bq2bq:latest", + Image: "raystack/bq2bq:latest", } jobService.On("Get", ctx, sampleTenant, jobA.Spec().Name()).Return(jobA, nil) jobService.On("GetTaskInfo", ctx, jobA.Spec().Task()).Return(taskInfo, nil) - handler := v1beta1.NewJobHandler(jobService, nil) + handler := v1beta1.NewJobHandler(jobService, log) resp, err := handler.GetJobTask(ctx, req) assert.NoError(t, err) assert.NotNil(t, resp) @@ -1852,6 +1988,24 @@ func (_m *JobService) Delete(ctx context.Context, jobTenant tenant.Tenant, jobNa return r0, r1 } +// ChangeNamespace provides a mock function with given fields: ctx, jobName, jobTenant, jobNewTenant +func (_m *JobService) ChangeNamespace(ctx context.Context, jobTenant, jobNewTenant tenant.Tenant, jobName job.Name) error { + ret := _m.Called(ctx, jobTenant, jobNewTenant, jobName) + return ret.Error(0) +} + +// UpdateState provides a mock function with given fields: ctx, jobTenant, jobNames, jobState, remark +func (_m *JobService) UpdateState(ctx context.Context, jobTenant tenant.Tenant, jobNames []job.Name, jobState job.State, remark string) error { + ret := _m.Called(ctx, jobTenant, jobNames, jobState, remark) + return ret.Error(0) +} + +// UpdateState provides a mock function with given fields: ctx, jobTenant, disabledJobNames, enabledJobNames +func (_m *JobService) SyncState(ctx context.Context, jobTenant tenant.Tenant, disabledJobNames, enabledJobNames []job.Name) error { + ret := _m.Called(ctx, jobTenant, disabledJobNames, enabledJobNames) + return ret.Error(0) +} + // Get provides a mock function with given fields: ctx, jobTenant, jobName func (_m *JobService) Get(ctx context.Context, jobTenant tenant.Tenant, jobName job.Name) (*job.Job, error) { ret := _m.Called(ctx, jobTenant, jobName) @@ -2011,13 +2165,13 @@ func (_m *JobService) Refresh(ctx context.Context, projectName tenant.ProjectNam return r0 } -// ReplaceAll provides a mock function with given fields: ctx, jobTenant, jobs, jobNamesWithValidationError, logWriter -func (_m *JobService) ReplaceAll(ctx context.Context, jobTenant tenant.Tenant, jobs []*job.Spec, jobNamesWithValidationError []job.Name, logWriter writer.LogWriter) error { - ret := _m.Called(ctx, jobTenant, jobs, jobNamesWithValidationError, logWriter) +// ReplaceAll provides a mock function with given fields: ctx, jobTenant, jobs, jobNamesWithInvalidSpec, logWriter +func (_m *JobService) ReplaceAll(ctx context.Context, jobTenant tenant.Tenant, jobs []*job.Spec, jobNamesWithInvalidSpec []job.Name, logWriter writer.LogWriter) error { + ret := _m.Called(ctx, jobTenant, jobs, jobNamesWithInvalidSpec, logWriter) var r0 error if rf, ok := ret.Get(0).(func(context.Context, tenant.Tenant, []*job.Spec, []job.Name, writer.LogWriter) error); ok { - r0 = rf(ctx, jobTenant, jobs, jobNamesWithValidationError, logWriter) + r0 = rf(ctx, jobTenant, jobs, jobNamesWithInvalidSpec, logWriter) } else { r0 = ret.Error(0) } @@ -2040,12 +2194,12 @@ func (_m *JobService) Update(ctx context.Context, jobTenant tenant.Tenant, jobs } // Validate provides a mock function with given fields: ctx, jobTenant, jobSpecs, logWriter -func (_m *JobService) Validate(ctx context.Context, jobTenant tenant.Tenant, jobSpecs []*job.Spec, logWriter writer.LogWriter) error { +func (_m *JobService) Validate(ctx context.Context, jobTenant tenant.Tenant, jobSpecs []*job.Spec, jobNamesWithInvalidSpec []job.Name, logWriter writer.LogWriter) error { ret := _m.Called(ctx, jobTenant, jobSpecs, logWriter) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, tenant.Tenant, []*job.Spec, writer.LogWriter) error); ok { - r0 = rf(ctx, jobTenant, jobSpecs, logWriter) + if rf, ok := ret.Get(0).(func(context.Context, tenant.Tenant, []*job.Spec, []job.Name, writer.LogWriter) error); ok { + r0 = rf(ctx, jobTenant, jobSpecs, jobNamesWithInvalidSpec, logWriter) } else { r0 = ret.Error(0) } diff --git a/core/job/job.go b/core/job/job.go index e30b51d60d..60ed3144b8 100644 --- a/core/job/job.go +++ b/core/job/job.go @@ -4,8 +4,8 @@ import ( "fmt" "strings" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" ) const ( @@ -16,6 +16,18 @@ const ( UpstreamStateResolved UpstreamState = "resolved" UpstreamStateUnresolved UpstreamState = "unresolved" + + MetricJobEvent = "job_events_total" + MetricJobEventStateAdded = "added" + MetricJobEventStateUpdated = "updated" + MetricJobEventStateDeleted = "deleted" + MetricJobEventStateUpsertFailed = "upsert_failed" + MetricJobEventStateDeleteFailed = "delete_failed" + MetricJobEventStateValidationFailed = "validation_failed" + MetricJobEventEnabled = "enabled" + MetricJobEventDisabled = "disabled" + + MetricJobRefreshResourceDownstream = "refresh_resource_downstream_total" ) type Job struct { @@ -87,7 +99,7 @@ func (j *Job) getStaticUpstreamsToResolve() ([]*Upstream, error) { unresolvedStaticUpstreams = append(unresolvedStaticUpstreams, NewUpstreamUnresolvedStatic(jobUpstreamName, projectUpstreamName)) } - return unresolvedStaticUpstreams, errors.MultiToError(me) + return unresolvedStaticUpstreams, me.ToErr() } type ResourceURN string @@ -171,7 +183,7 @@ func (j Jobs) GetJobsWithUnresolvedUpstreams() ([]*WithUpstream, error) { jobsWithUnresolvedUpstream = append(jobsWithUnresolvedUpstream, jobWithUnresolvedUpstream) } - return jobsWithUnresolvedUpstream, errors.MultiToError(me) + return jobsWithUnresolvedUpstream, me.ToErr() } type WithUpstream struct { diff --git a/core/job/job_test.go b/core/job/job_test.go index 73bd0f3752..45c96f9faf 100644 --- a/core/job/job_test.go +++ b/core/job/job_test.go @@ -5,9 +5,9 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/job" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/models" + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/models" ) func TestEntityJob(t *testing.T) { diff --git a/core/job/resolver/external_upstream_resolver.go b/core/job/resolver/external_upstream_resolver.go index 240a1b0de8..bfc5d5cb1a 100644 --- a/core/job/resolver/external_upstream_resolver.go +++ b/core/job/resolver/external_upstream_resolver.go @@ -6,11 +6,11 @@ import ( "github.com/kushsharma/parallel" - "github.com/odpf/optimus/config" - "github.com/odpf/optimus/core/job" - "github.com/odpf/optimus/ext/resourcemanager" - "github.com/odpf/optimus/internal/errors" - "github.com/odpf/optimus/internal/writer" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/ext/resourcemanager" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/writer" ) type extUpstreamResolver struct { @@ -57,12 +57,12 @@ func (e *extUpstreamResolver) Resolve(ctx context.Context, jobWithUpstream *job. resolvedExternally = true } if len(me.Errors) > 0 { - lw.Write(writer.LogLevelError, errors.MultiToError(me).Error()) + lw.Write(writer.LogLevelError, me.ToErr().Error()) } if resolvedExternally { lw.Write(writer.LogLevelDebug, fmt.Sprintf("[%s] resolved job %s upstream from external", jobWithUpstream.Job().Tenant().NamespaceName().String(), jobWithUpstream.Name().String())) } - return job.NewWithUpstream(jobWithUpstream.Job(), mergedUpstreams), errors.MultiToError(me) + return job.NewWithUpstream(jobWithUpstream.Job(), mergedUpstreams), me.ToErr() } func (e *extUpstreamResolver) BulkResolve(ctx context.Context, jobsWithUpstream []*job.WithUpstream, lw writer.LogWriter) ([]*job.WithUpstream, error) { @@ -90,7 +90,7 @@ func (e *extUpstreamResolver) BulkResolve(ctx context.Context, jobsWithUpstream me.Append(result.Err) } - return jobsWithAllUpstream, errors.MultiToError(me) + return jobsWithAllUpstream, me.ToErr() } func (e *extUpstreamResolver) fetchOptimusUpstreams(ctx context.Context, unresolvedUpstream *job.Upstream) ([]*job.Upstream, error) { @@ -104,7 +104,7 @@ func (e *extUpstreamResolver) fetchOptimusUpstreams(ctx context.Context, unresol } upstreams = append(upstreams, deps...) } - return upstreams, errors.MultiToError(me) + return upstreams, me.ToErr() } func NewTestExternalUpstreamResolver( diff --git a/core/job/resolver/external_upstream_resolver_test.go b/core/job/resolver/external_upstream_resolver_test.go index fcd5cf0ab1..9da06c292e 100644 --- a/core/job/resolver/external_upstream_resolver_test.go +++ b/core/job/resolver/external_upstream_resolver_test.go @@ -8,12 +8,12 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/config" - "github.com/odpf/optimus/core/job" - "github.com/odpf/optimus/core/job/resolver" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/ext/resourcemanager" - "github.com/odpf/optimus/internal/models" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/core/job/resolver" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/ext/resourcemanager" + "github.com/raystack/optimus/internal/models" ) func TestExternalUpstreamResolver(t *testing.T) { diff --git a/core/job/resolver/internal_upstream_resolver.go b/core/job/resolver/internal_upstream_resolver.go index 23210de7b3..0ceccfafb0 100644 --- a/core/job/resolver/internal_upstream_resolver.go +++ b/core/job/resolver/internal_upstream_resolver.go @@ -5,9 +5,9 @@ import ( "golang.org/x/net/context" - "github.com/odpf/optimus/core/job" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" ) type internalUpstreamResolver struct { @@ -49,7 +49,7 @@ func (i internalUpstreamResolver) Resolve(ctx context.Context, jobWithUnresolved } distinctUpstreams := job.Upstreams(upstreamResults).Deduplicate() - return job.NewWithUpstream(jobWithUnresolvedUpstream.Job(), distinctUpstreams), errors.MultiToError(me) + return job.NewWithUpstream(jobWithUnresolvedUpstream.Job(), distinctUpstreams), me.ToErr() } func (i internalUpstreamResolver) BulkResolve(ctx context.Context, projectName tenant.ProjectName, jobsWithUnresolvedUpstream []*job.WithUpstream) ([]*job.WithUpstream, error) { @@ -77,7 +77,7 @@ func (i internalUpstreamResolver) resolveInferredUpstream(ctx context.Context, s upstream := job.NewUpstreamResolved(jobUpstreams[0].Spec().Name(), "", jobUpstreams[0].Destination(), jobUpstreams[0].Tenant(), job.UpstreamTypeInferred, jobUpstreams[0].Spec().Task().Name(), false) internalUpstream = append(internalUpstream, upstream) } - return internalUpstream, errors.MultiToError(me) + return internalUpstream, me.ToErr() } func (i internalUpstreamResolver) resolveStaticUpstream(ctx context.Context, projectName tenant.ProjectName, upstreamSpec *job.UpstreamSpec) ([]*job.Upstream, error) { @@ -97,5 +97,5 @@ func (i internalUpstreamResolver) resolveStaticUpstream(ctx context.Context, pro upstream := job.NewUpstreamResolved(upstreamJobName, "", jobUpstream.Destination(), jobUpstream.Tenant(), job.UpstreamTypeStatic, jobUpstream.Spec().Task().Name(), false) internalUpstream = append(internalUpstream, upstream) } - return internalUpstream, errors.MultiToError(me) + return internalUpstream, me.ToErr() } diff --git a/core/job/resolver/internal_upstream_resolver_test.go b/core/job/resolver/internal_upstream_resolver_test.go index 6b20ffe31b..ea4951ecf6 100644 --- a/core/job/resolver/internal_upstream_resolver_test.go +++ b/core/job/resolver/internal_upstream_resolver_test.go @@ -7,10 +7,10 @@ import ( "github.com/stretchr/testify/assert" "golang.org/x/net/context" - "github.com/odpf/optimus/core/job" - "github.com/odpf/optimus/core/job/resolver" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/models" + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/core/job/resolver" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/models" ) func TestInternalUpstreamResolver(t *testing.T) { diff --git a/core/job/resolver/upstream_resolver.go b/core/job/resolver/upstream_resolver.go index f861f797ab..c33b4f60dd 100644 --- a/core/job/resolver/upstream_resolver.go +++ b/core/job/resolver/upstream_resolver.go @@ -4,15 +4,15 @@ import ( "context" "fmt" - "github.com/odpf/optimus/core/job" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" - "github.com/odpf/optimus/internal/writer" + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/writer" ) const ( - ConcurrentTicketPerSec = 40 - ConcurrentLimit = 600 + ConcurrentTicketPerSec = 50 + ConcurrentLimit = 100 ) type UpstreamResolver struct { @@ -57,7 +57,7 @@ func (u UpstreamResolver) BulkResolve(ctx context.Context, projectName tenant.Pr errorMsg := fmt.Sprintf("unable to resolve upstream: %s", err.Error()) logWriter.Write(writer.LogLevelError, errorMsg) me.Append(errors.NewError(errors.ErrInternalError, job.EntityJob, errorMsg)) - return nil, errors.MultiToError(me) + return nil, me.ToErr() } jobsWithResolvedExternalUpstreams, err := u.externalUpstreamResolver.BulkResolve(ctx, jobsWithResolvedInternalUpstreams, logWriter) @@ -65,7 +65,7 @@ func (u UpstreamResolver) BulkResolve(ctx context.Context, projectName tenant.Pr me.Append(u.getUnresolvedUpstreamsErrors(jobsWithResolvedExternalUpstreams, logWriter)) - return jobsWithResolvedExternalUpstreams, errors.MultiToError(me) + return jobsWithResolvedExternalUpstreams, me.ToErr() } func (u UpstreamResolver) Resolve(ctx context.Context, subjectJob *job.Job, logWriter writer.LogWriter) ([]*job.Upstream, error) { @@ -80,7 +80,7 @@ func (u UpstreamResolver) Resolve(ctx context.Context, subjectJob *job.Job, logW jobWithInternalExternalUpstream, err := u.externalUpstreamResolver.Resolve(ctx, jobWithInternalUpstream, logWriter) me.Append(err) - return jobWithInternalExternalUpstream.Upstreams(), errors.MultiToError(me) + return jobWithInternalExternalUpstream.Upstreams(), me.ToErr() } func (UpstreamResolver) getUnresolvedUpstreamsErrors(jobsWithUpstreams []*job.WithUpstream, logWriter writer.LogWriter) error { @@ -94,5 +94,5 @@ func (UpstreamResolver) getUnresolvedUpstreamsErrors(jobsWithUpstreams []*job.Wi } } } - return errors.MultiToError(me) + return me.ToErr() } diff --git a/core/job/resolver/upstream_resolver_test.go b/core/job/resolver/upstream_resolver_test.go index ac3c16e0d2..8389aabfb2 100644 --- a/core/job/resolver/upstream_resolver_test.go +++ b/core/job/resolver/upstream_resolver_test.go @@ -8,11 +8,11 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/core/job" - "github.com/odpf/optimus/core/job/resolver" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/models" - "github.com/odpf/optimus/internal/writer" + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/core/job/resolver" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/models" + "github.com/raystack/optimus/internal/writer" ) func TestUpstreamResolver(t *testing.T) { diff --git a/core/job/resolver/util.go b/core/job/resolver/util.go index 657d4e1aa2..38760e0f47 100644 --- a/core/job/resolver/util.go +++ b/core/job/resolver/util.go @@ -1,6 +1,6 @@ package resolver -import "github.com/odpf/optimus/core/job" +import "github.com/raystack/optimus/core/job" func mergeUpstreams(upstreamGroups ...[]*job.Upstream) []*job.Upstream { var allUpstreams []*job.Upstream diff --git a/core/job/service/filter/filter_test.go b/core/job/service/filter/filter_test.go index a6312edb27..a7225f1c73 100644 --- a/core/job/service/filter/filter_test.go +++ b/core/job/service/filter/filter_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/job/service/filter" + "github.com/raystack/optimus/core/job/service/filter" ) func TestFilter(t *testing.T) { diff --git a/core/job/service/job_service.go b/core/job/service/job_service.go index f1bb78bb0a..20e33e6b1e 100644 --- a/core/job/service/job_service.go +++ b/core/job/service/job_service.go @@ -7,41 +7,59 @@ import ( "strings" "github.com/kushsharma/parallel" - "github.com/odpf/salt/log" - - "github.com/odpf/optimus/core/job" - "github.com/odpf/optimus/core/job/service/filter" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" - "github.com/odpf/optimus/internal/lib/tree" - "github.com/odpf/optimus/internal/telemetry" - "github.com/odpf/optimus/internal/writer" - "github.com/odpf/optimus/sdk/plugin" + "github.com/raystack/salt/log" + + "github.com/raystack/optimus/core/event" + "github.com/raystack/optimus/core/event/moderator" + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/core/job/service/filter" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/lib/tree" + "github.com/raystack/optimus/internal/telemetry" + "github.com/raystack/optimus/internal/writer" + "github.com/raystack/optimus/sdk/plugin" ) const ( - ConcurrentTicketPerSec = 40 - ConcurrentLimit = 600 + ConcurrentTicketPerSec = 50 + ConcurrentLimit = 100 ) type JobService struct { - repo JobRepository + jobRepo JobRepository + upstreamRepo UpstreamRepository + downstreamRepo DownstreamRepository pluginService PluginService upstreamResolver UpstreamResolver + eventHandler EventHandler + scheduler Scheduler tenantDetailsGetter TenantDetailsGetter + jobDeploymentService JobDeploymentService + logger log.Logger } -func NewJobService(repo JobRepository, pluginService PluginService, upstreamResolver UpstreamResolver, tenantDetailsGetter TenantDetailsGetter, logger log.Logger) *JobService { +func NewJobService( + jobRepo JobRepository, upstreamRepo UpstreamRepository, downstreamRepo DownstreamRepository, + pluginService PluginService, upstreamResolver UpstreamResolver, + tenantDetailsGetter TenantDetailsGetter, eventHandler EventHandler, logger log.Logger, + jobDeploymentService JobDeploymentService, scheduler Scheduler, +) *JobService { return &JobService{ - repo: repo, - pluginService: pluginService, - upstreamResolver: upstreamResolver, - tenantDetailsGetter: tenantDetailsGetter, - logger: logger, + jobRepo: jobRepo, + upstreamRepo: upstreamRepo, + downstreamRepo: downstreamRepo, + pluginService: pluginService, + upstreamResolver: upstreamResolver, + eventHandler: eventHandler, + tenantDetailsGetter: tenantDetailsGetter, + logger: logger, + jobDeploymentService: jobDeploymentService, + scheduler: scheduler, } } @@ -55,23 +73,40 @@ type TenantDetailsGetter interface { GetDetails(ctx context.Context, jobTenant tenant.Tenant) (*tenant.WithDetails, error) } +type JobDeploymentService interface { + UploadJobs(ctx context.Context, jobTenant tenant.Tenant, toUpdate, toDelete []string) error +} + type JobRepository interface { // TODO: remove `savedJobs` since the method's main purpose is to add, not to get Add(context.Context, []*job.Job) (addedJobs []*job.Job, err error) Update(context.Context, []*job.Job) (updatedJobs []*job.Job, err error) Delete(ctx context.Context, projectName tenant.ProjectName, jobName job.Name, cleanHistory bool) error + ChangeJobNamespace(ctx context.Context, jobName job.Name, tenant, newTenant tenant.Tenant) error + GetByJobName(ctx context.Context, projectName tenant.ProjectName, jobName job.Name) (*job.Job, error) GetAllByResourceDestination(ctx context.Context, resourceDestination job.ResourceURN) ([]*job.Job, error) GetAllByTenant(ctx context.Context, jobTenant tenant.Tenant) ([]*job.Job, error) GetAllByProjectName(ctx context.Context, projectName tenant.ProjectName) ([]*job.Job, error) + SyncState(ctx context.Context, jobTenant tenant.Tenant, disabledJobNames, enabledJobNames []job.Name) error + UpdateState(ctx context.Context, jobTenant tenant.Tenant, jobNames []job.Name, jobState job.State, remark string) error +} +type UpstreamRepository interface { ResolveUpstreams(context.Context, tenant.ProjectName, []job.Name) (map[job.Name][]*job.Upstream, error) ReplaceUpstreams(context.Context, []*job.WithUpstream) error GetUpstreams(ctx context.Context, projectName tenant.ProjectName, jobName job.Name) ([]*job.Upstream, error) +} +type DownstreamRepository interface { GetDownstreamByDestination(ctx context.Context, projectName tenant.ProjectName, destination job.ResourceURN) ([]*job.Downstream, error) GetDownstreamByJobName(ctx context.Context, projectName tenant.ProjectName, jobName job.Name) ([]*job.Downstream, error) + GetDownstreamBySources(ctx context.Context, sources []job.ResourceURN) ([]*job.Downstream, error) +} + +type EventHandler interface { + HandleEvent(moderator.Event) } type UpstreamResolver interface { @@ -79,28 +114,46 @@ type UpstreamResolver interface { Resolve(ctx context.Context, subjectJob *job.Job, logWriter writer.LogWriter) ([]*job.Upstream, error) } +type Scheduler interface { + UpdateJobState(ctx context.Context, tnnt tenant.Tenant, jobName []job.Name, state string) error +} + func (j *JobService) Add(ctx context.Context, jobTenant tenant.Tenant, specs []*job.Spec) error { logWriter := writer.NewLogWriter(j.logger) me := errors.NewMultiError("add specs errors") tenantWithDetails, err := j.tenantDetailsGetter.GetDetails(ctx, jobTenant) if err != nil { + j.logger.Error("error getting tenant details: %s", err) return err } jobs, err := j.generateJobs(ctx, tenantWithDetails, specs, logWriter) me.Append(err) - addedJobs, err := j.repo.Add(ctx, jobs) + addedJobs, err := j.jobRepo.Add(ctx, jobs) me.Append(err) jobsWithUpstreams, err := j.upstreamResolver.BulkResolve(ctx, jobTenant.ProjectName(), addedJobs, logWriter) me.Append(err) - err = j.repo.ReplaceUpstreams(ctx, jobsWithUpstreams) + err = j.upstreamRepo.ReplaceUpstreams(ctx, jobsWithUpstreams) me.Append(err) - return errors.MultiToError(me) + err = j.uploadJobs(ctx, jobTenant, addedJobs, nil, nil) + me.Append(err) + + for _, job := range addedJobs { + j.raiseCreateEvent(job) + } + raiseJobEventMetric(jobTenant, job.MetricJobEventStateAdded, len(addedJobs)) + + if len(addedJobs) < len(specs) { + totalFailed := len(specs) - len(addedJobs) + raiseJobEventMetric(jobTenant, job.MetricJobEventStateUpsertFailed, totalFailed) + } + + return me.ToErr() } func (j *JobService) Update(ctx context.Context, jobTenant tenant.Tenant, specs []*job.Spec) error { @@ -109,38 +162,128 @@ func (j *JobService) Update(ctx context.Context, jobTenant tenant.Tenant, specs tenantWithDetails, err := j.tenantDetailsGetter.GetDetails(ctx, jobTenant) if err != nil { + j.logger.Error("error getting tenant details: %s", err) return err } jobs, err := j.generateJobs(ctx, tenantWithDetails, specs, logWriter) me.Append(err) - updatedJobs, err := j.repo.Update(ctx, jobs) + updatedJobs, err := j.jobRepo.Update(ctx, jobs) me.Append(err) jobsWithUpstreams, err := j.upstreamResolver.BulkResolve(ctx, jobTenant.ProjectName(), updatedJobs, logWriter) me.Append(err) - err = j.repo.ReplaceUpstreams(ctx, jobsWithUpstreams) + err = j.upstreamRepo.ReplaceUpstreams(ctx, jobsWithUpstreams) me.Append(err) - return errors.MultiToError(me) + err = j.uploadJobs(ctx, jobTenant, nil, updatedJobs, nil) + me.Append(err) + + for _, job := range updatedJobs { + j.raiseUpdateEvent(job) + } + raiseJobEventMetric(jobTenant, job.MetricJobEventStateUpdated, len(updatedJobs)) + + if len(updatedJobs) < len(specs) { + totalFailed := len(specs) - len(updatedJobs) + raiseJobEventMetric(jobTenant, job.MetricJobEventStateUpsertFailed, totalFailed) + } + + return me.ToErr() +} + +func (j *JobService) UpdateState(ctx context.Context, jobTenant tenant.Tenant, jobNames []job.Name, jobState job.State, remark string) error { + err := j.scheduler.UpdateJobState(ctx, jobTenant, jobNames, jobState.String()) + if err != nil { + return err + } + + if err := j.jobRepo.UpdateState(ctx, jobTenant, jobNames, jobState, remark); err != nil { + return err + } + + var metricName string + switch jobState { + case job.ENABLED: + metricName = job.MetricJobEventEnabled + case job.DISABLED: + metricName = job.MetricJobEventDisabled + } + + raiseJobEventMetric(jobTenant, metricName, len(jobNames)) + for _, jobName := range jobNames { + j.raiseStateChangeEvent(jobTenant, jobName, jobState) + } + return nil +} + +func (j *JobService) SyncState(ctx context.Context, jobTenant tenant.Tenant, disabledJobNames, enabledJobNames []job.Name) error { + return j.jobRepo.SyncState(ctx, jobTenant, disabledJobNames, enabledJobNames) } func (j *JobService) Delete(ctx context.Context, jobTenant tenant.Tenant, jobName job.Name, cleanFlag, forceFlag bool) (affectedDownstream []job.FullName, err error) { - downstreamList, err := j.repo.GetDownstreamByJobName(ctx, jobTenant.ProjectName(), jobName) + downstreamList, err := j.downstreamRepo.GetDownstreamByJobName(ctx, jobTenant.ProjectName(), jobName) if err != nil { + raiseJobEventMetric(jobTenant, job.MetricJobEventStateDeleteFailed, 1) + j.logger.Error("error getting downstream jobs for [%s]: %s", jobName, err) return nil, err } downstreamFullNames := job.DownstreamList(downstreamList).GetDownstreamFullNames() if len(downstreamList) > 0 && !forceFlag { + raiseJobEventMetric(jobTenant, job.MetricJobEventStateDeleteFailed, 1) errorMsg := fmt.Sprintf("%s depends on this job. consider do force delete to proceed.", downstreamFullNames) + j.logger.Error(errorMsg) return nil, errors.NewError(errors.ErrFailedPrecond, job.EntityJob, errorMsg) } - return downstreamFullNames, j.repo.Delete(ctx, jobTenant.ProjectName(), jobName, cleanFlag) + if err := j.jobRepo.Delete(ctx, jobTenant.ProjectName(), jobName, cleanFlag); err != nil { + raiseJobEventMetric(jobTenant, job.MetricJobEventStateDeleteFailed, 1) + j.logger.Error("error deleting job [%s]: %s", jobName, err) + return downstreamFullNames, err + } + + raiseJobEventMetric(jobTenant, job.MetricJobEventStateDeleted, 1) + + if err := j.uploadJobs(ctx, jobTenant, nil, nil, []job.Name{jobName}); err != nil { + j.logger.Error("error uploading job [%s]: %s", jobName, err) + return downstreamFullNames, err + } + + j.raiseDeleteEvent(jobTenant, jobName) + + return downstreamFullNames, nil +} + +func (j *JobService) ChangeNamespace(ctx context.Context, jobTenant, jobNewTenant tenant.Tenant, jobName job.Name) error { + err := j.jobRepo.ChangeJobNamespace(ctx, jobName, jobTenant, jobNewTenant) + if err != nil { + errorsMsg := fmt.Sprintf("unable to successfully finish job namespace change transaction : %s", err.Error()) + return errors.NewError(errors.ErrInternalError, job.EntityJob, errorsMsg) + } + + newJobSpec, err := j.jobRepo.GetByJobName(ctx, jobNewTenant.ProjectName(), jobName) + if err != nil { + errorsMsg := fmt.Sprintf(" unable fetch jobSpecs for newly modified job : %s, namespace: %s, err: %s", jobName, jobNewTenant.NamespaceName(), err.Error()) + return errors.NewError(errors.ErrInternalError, job.EntityJob, errorsMsg) + } + + err = j.uploadJobs(ctx, jobTenant, nil, nil, []job.Name{jobName}) + if err != nil { + errorsMsg := fmt.Sprintf(" unable to remove old job : %s", err.Error()) + return errors.NewError(errors.ErrInternalError, job.EntityJob, errorsMsg) + } + + err = j.uploadJobs(ctx, jobNewTenant, []*job.Job{newJobSpec}, nil, nil) + if err != nil { + errorsMsg := fmt.Sprintf(" unable to create new job on scheduler : %s", err.Error()) + return errors.NewError(errors.ErrInternalError, job.EntityJob, errorsMsg) + } + j.raiseUpdateEvent(newJobSpec) + return nil } func (j *JobService) Get(ctx context.Context, jobTenant tenant.Tenant, jobName job.Name) (*job.Job, error) { @@ -149,9 +292,11 @@ func (j *JobService) Get(ctx context.Context, jobTenant tenant.Tenant, jobName j filter.WithString(filter.JobName, jobName.String()), ) if err != nil { + j.logger.Error("error getting job specified by the filter: %s", err) return nil, err } if len(jobs) == 0 { + j.logger.Error("job [%s] is not found", jobName) return nil, errors.NotFound(job.EntityJob, fmt.Sprintf("job %s is not found", jobName)) } return jobs[0], nil @@ -166,12 +311,16 @@ func (j *JobService) GetByFilter(ctx context.Context, filters ...filter.FilterOp // when resource destination exist, filter by destination if f.Contains(filter.ResourceDestination) { + j.logger.Debug("getting all jobs by resource destination [%s]", f.GetStringValue(filter.ResourceDestination)) + resourceDestination := job.ResourceURN(f.GetStringValue(filter.ResourceDestination)) - return j.repo.GetAllByResourceDestination(ctx, resourceDestination) + return j.jobRepo.GetAllByResourceDestination(ctx, resourceDestination) } // when project name and job names exist, filter by project and job names if f.Contains(filter.ProjectName, filter.JobNames) { + j.logger.Debug("getting all jobs by project name [%s] and job names", f.GetStringValue(filter.ProjectName)) + me := errors.NewMultiError("get all job specs errors") projectName, _ := tenant.ProjectNameFrom(f.GetStringValue(filter.ProjectName)) @@ -180,27 +329,31 @@ func (j *JobService) GetByFilter(ctx context.Context, filters ...filter.FilterOp var jobs []*job.Job for _, jobNameStr := range jobNames { jobName, _ := job.NameFrom(jobNameStr) - fetchedJob, err := j.repo.GetByJobName(ctx, projectName, jobName) + fetchedJob, err := j.jobRepo.GetByJobName(ctx, projectName, jobName) if err != nil { if !errors.IsErrorType(err, errors.ErrNotFound) { + j.logger.Error("error getting job [%s] from db: %s", jobName, err) me.Append(err) } continue } jobs = append(jobs, fetchedJob) } - return jobs, errors.MultiToError(me) + return jobs, me.ToErr() } // when project name and job name exist, filter by project name and job name if f.Contains(filter.ProjectName, filter.JobName) { + j.logger.Debug("getting all jobs by project name [%s] and job name [%s]", f.GetStringValue(filter.ProjectName), f.GetStringValue(filter.JobName)) + projectName, _ := tenant.ProjectNameFrom(f.GetStringValue(filter.ProjectName)) jobName, _ := job.NameFrom(f.GetStringValue(filter.JobName)) - fetchedJob, err := j.repo.GetByJobName(ctx, projectName, jobName) + fetchedJob, err := j.jobRepo.GetByJobName(ctx, projectName, jobName) if err != nil { if errors.IsErrorType(err, errors.ErrNotFound) { return []*job.Job{}, nil } + j.logger.Error("error getting job [%s] from db: %s", jobName, err) return nil, err } return []*job.Job{fetchedJob}, nil @@ -208,15 +361,19 @@ func (j *JobService) GetByFilter(ctx context.Context, filters ...filter.FilterOp // when project name and namespace names exist, filter by tenant if f.Contains(filter.ProjectName, filter.NamespaceNames) { + j.logger.Debug("getting all jobs by project name [%s] and namespace names", f.GetStringValue(filter.ProjectName)) + var jobs []*job.Job namespaceNames := f.GetStringArrayValue(filter.NamespaceNames) for _, namespaceName := range namespaceNames { jobTenant, err := tenant.NewTenant(f.GetStringValue(filter.ProjectName), namespaceName) if err != nil { + j.logger.Error("invalid tenant request information project [%s] namespace [%s]: %s", f.GetStringValue(filter.ProjectName), f.GetStringValue(filter.NamespaceName), err) return nil, err } - tenantJobs, err := j.repo.GetAllByTenant(ctx, jobTenant) + tenantJobs, err := j.jobRepo.GetAllByTenant(ctx, jobTenant) if err != nil { + j.logger.Error("error getting all jobs under project [%s] namespace [%s]: %s", jobTenant.ProjectName().String(), jobTenant.NamespaceName().String(), err) return nil, err } jobs = append(jobs, tenantJobs...) @@ -226,48 +383,83 @@ func (j *JobService) GetByFilter(ctx context.Context, filters ...filter.FilterOp // when project name and namespace name exist, filter by tenant if f.Contains(filter.ProjectName, filter.NamespaceName) { + j.logger.Debug("getting all jobs by project name [%s] and namespace name [%s]", f.GetStringValue(filter.ProjectName), f.GetStringValue(filter.NamespaceName)) + jobTenant, err := tenant.NewTenant(f.GetStringValue(filter.ProjectName), f.GetStringValue(filter.NamespaceName)) if err != nil { + j.logger.Error("invalid tenant request information project [%s] namespace [%s]: %s", f.GetStringValue(filter.ProjectName), f.GetStringValue(filter.NamespaceName), err) return nil, err } - return j.repo.GetAllByTenant(ctx, jobTenant) + return j.jobRepo.GetAllByTenant(ctx, jobTenant) } // when project name exist, filter by project name if f.Contains(filter.ProjectName) { + j.logger.Debug("getting all jobs by project name [%s]", f.GetStringValue(filter.ProjectName)) + projectName, _ := tenant.ProjectNameFrom(f.GetStringValue(filter.ProjectName)) - return j.repo.GetAllByProjectName(ctx, projectName) + return j.jobRepo.GetAllByProjectName(ctx, projectName) } + j.logger.Error("filter combination is not recognized") return nil, fmt.Errorf("no filter matched") } -func (j *JobService) ReplaceAll(ctx context.Context, jobTenant tenant.Tenant, specs []*job.Spec, jobNamesWithValidationError []job.Name, logWriter writer.LogWriter) error { +func (j *JobService) ReplaceAll(ctx context.Context, jobTenant tenant.Tenant, specs []*job.Spec, jobNamesWithInvalidSpec []job.Name, logWriter writer.LogWriter) error { me := errors.NewMultiError("replace all specs errors") - toAdd, toUpdate, toDelete, err := j.differentiateSpecs(ctx, jobTenant, specs, jobNamesWithValidationError) + existingJobs, err := j.jobRepo.GetAllByTenant(ctx, jobTenant) + me.Append(err) + + toAdd, toUpdate, toDelete, _, err := j.differentiateSpecs(existingJobs, specs, jobNamesWithInvalidSpec) logWriter.Write(writer.LogLevelInfo, fmt.Sprintf("[%s] found %d new, %d modified, and %d deleted job specs", jobTenant.NamespaceName().String(), len(toAdd), len(toUpdate), len(toDelete))) me.Append(err) tenantWithDetails, err := j.tenantDetailsGetter.GetDetails(ctx, jobTenant) if err != nil { + j.logger.Error("error getting tenant details: %s", err) me.Append(err) - return errors.MultiToError(me) + return me.ToErr() } addedJobs, err := j.bulkAdd(ctx, tenantWithDetails, toAdd, logWriter) me.Append(err) + failedToAdd := len(toAdd) - len(addedJobs) updatedJobs, err := j.bulkUpdate(ctx, tenantWithDetails, toUpdate, logWriter) me.Append(err) + failedToUpdate := len(toUpdate) - len(updatedJobs) - err = j.bulkDelete(ctx, jobTenant, toDelete, logWriter) + deletedJobNames, err := j.bulkDelete(ctx, jobTenant, toDelete, logWriter) me.Append(err) err = j.resolveAndSaveUpstreams(ctx, jobTenant, logWriter, addedJobs, updatedJobs) me.Append(err) - return errors.MultiToError(me) + err = j.uploadJobs(ctx, jobTenant, addedJobs, updatedJobs, deletedJobNames) + me.Append(err) + + raiseJobEventMetric(tenantWithDetails.ToTenant(), job.MetricJobEventStateUpsertFailed, failedToAdd+failedToUpdate) + + return me.ToErr() +} + +func (j *JobService) uploadJobs(ctx context.Context, jobTenant tenant.Tenant, addedJobs, updatedJobs []*job.Job, deletedJobNames []job.Name) error { + if len(addedJobs) == 0 && len(updatedJobs) == 0 && len(deletedJobNames) == 0 { + j.logger.Warn("no jobs to be uploaded") + return nil + } + + var jobNamesToUpload, jobNamesToRemove []string + for _, addedJob := range append(addedJobs, updatedJobs...) { + jobNamesToUpload = append(jobNamesToUpload, addedJob.GetName()) + } + + for _, deletedJobName := range deletedJobNames { + jobNamesToRemove = append(jobNamesToRemove, deletedJobName.String()) + } + + return j.jobDeploymentService.UploadJobs(ctx, jobTenant, jobNamesToUpload, jobNamesToRemove) } func (j *JobService) Refresh(ctx context.Context, projectName tenant.ProjectName, namespaceNames, jobNames []string, logWriter writer.LogWriter) (err error) { @@ -277,6 +469,7 @@ func (j *JobService) Refresh(ctx context.Context, projectName tenant.ProjectName allJobs, err := j.GetByFilter(ctx, projectFilter, namespacesFilter, jobNamesFilter) if err != nil { + j.logger.Error("error getting jobs by filter: %s", err) return err } @@ -285,12 +478,14 @@ func (j *JobService) Refresh(ctx context.Context, projectName tenant.ProjectName for namespaceName, jobs := range namespaceAndJobsMap { jobTenant, err := tenant.NewTenant(projectName.String(), namespaceName.String()) if err != nil { + j.logger.Error("invalid tenant information requet project [%s] namespace [%s]: %s", projectName.String(), namespaceName.String(), err) me.Append(err) continue } tenantWithDetails, err := j.tenantDetailsGetter.GetDetails(ctx, jobTenant) if err != nil { + j.logger.Error("error getting tenant details: %s", err) me.Append(err) continue } @@ -299,54 +494,192 @@ func (j *JobService) Refresh(ctx context.Context, projectName tenant.ProjectName updatedJobs, err := j.bulkUpdate(ctx, tenantWithDetails, specs, logWriter) me.Append(err) - j.logger.Debug("resolving upstreams for %d jobs of project [%s] namespace [%s]", len(updatedJobs), projectName, namespaceName) + j.logger.Debug("resolving upstreams for [%d] jobs of project [%s] namespace [%s]", len(updatedJobs), projectName, namespaceName) jobsWithUpstreams, err := j.upstreamResolver.BulkResolve(ctx, projectName, updatedJobs, logWriter) me.Append(err) - j.logger.Debug("replacing upstreams for %d jobs of project [%s] namespace [%s]", len(jobsWithUpstreams), projectName, namespaceName) - err = j.repo.ReplaceUpstreams(ctx, jobsWithUpstreams) + err = j.upstreamRepo.ReplaceUpstreams(ctx, jobsWithUpstreams) + me.Append(err) + + j.logger.Debug("uploading [%d] jobs of project [%s] namespace [%s] to scheduler", len(jobs), projectName, namespaceName) + err = j.uploadJobs(ctx, jobTenant, jobs, nil, nil) me.Append(err) } - return errors.MultiToError(me) + return me.ToErr() } -func (j *JobService) Validate(ctx context.Context, jobTenant tenant.Tenant, jobSpecs []*job.Spec, logWriter writer.LogWriter) error { +func (j *JobService) RefreshResourceDownstream(ctx context.Context, resourceURNs []job.ResourceURN, logWriter writer.LogWriter) error { + downstreams, err := j.downstreamRepo.GetDownstreamBySources(ctx, resourceURNs) + if err != nil { + j.logger.Error("error identifying job downstream for given resources: %s", err) + return err + } + + groupedDownstreams := j.groupDownstreamPerProject(downstreams) + + me := errors.NewMultiError("refresh downstream errors") + for projectName, downstreams := range groupedDownstreams { + jobNames := make([]string, len(downstreams)) + for i, d := range downstreams { + jobNames[i] = d.Name().String() + } + + status := "successful" + if err := j.Refresh(ctx, projectName, nil, jobNames, logWriter); err != nil { + j.logger.Error("error refreshing downstream jobs for project [%s]: %s", projectName, err) + me.Append(err) + + status = "failed" + } + + counter := telemetry.NewCounter(job.MetricJobRefreshResourceDownstream, map[string]string{ + "project": projectName.String(), + "status": status, + }) + counter.Add(float64(len(jobNames))) + } + + return me.ToErr() +} + +func (j *JobService) Validate(ctx context.Context, jobTenant tenant.Tenant, jobSpecs []*job.Spec, jobNamesWithInvalidSpec []job.Name, logWriter writer.LogWriter) error { me := errors.NewMultiError("validate specs errors") tenantWithDetails, err := j.tenantDetailsGetter.GetDetails(ctx, jobTenant) if err != nil { + j.logger.Error("error getting tenant details: %s", err) return err } - jobs, err := j.generateJobs(ctx, tenantWithDetails, jobSpecs, logWriter) + err = job.Specs(jobSpecs).Validate() me.Append(err) + validatedJobSpecs := job.Specs(jobSpecs).GetValid() - jobsWithUnresolvedUpstreams, err := job.Jobs(jobs).GetJobsWithUnresolvedUpstreams() + existingJobs, err := j.jobRepo.GetAllByTenant(ctx, jobTenant) me.Append(err) - if len(me.Errors) > 0 { - return errors.MultiToError(me) - } + toAdd, toUpdate, toDelete, unmodifiedSpecs, err := j.differentiateSpecs(existingJobs, validatedJobSpecs, jobNamesWithInvalidSpec) + logWriter.Write(writer.LogLevelInfo, fmt.Sprintf("[%s] found %d new, %d modified, and %d deleted job specs", jobTenant.NamespaceName().String(), len(toAdd), len(toUpdate), len(toDelete))) + me.Append(err) + + incomingJobs, err := j.generateJobs(ctx, tenantWithDetails, append(toAdd, toUpdate...), logWriter) + me.Append(err) + + err = j.validateDeleteJobs(ctx, jobTenant, toDelete, logWriter) + me.Append(err) // NOTE: only check cyclic deps across internal upstreams (sources), need further discussion to check cyclic deps for external upstream // assumption, all job specs from input are also the job within same project + jobsToValidateMap := getAllJobsToValidateMap(incomingJobs, existingJobs, unmodifiedSpecs) + identifierToJobsMap := getIdentifierToJobsMap(jobsToValidateMap) + for _, jobEntity := range jobsToValidateMap { + if _, err := j.validateCyclic(jobEntity.Job().Spec().Name(), jobsToValidateMap, identifierToJobsMap); err != nil { + j.logger.Error("error when executing cyclic validation on [%s]: %s", jobEntity.Job().Spec().Name(), err) + me.Append(err) + break + } + } + + return me.ToErr() +} + +func (j *JobService) validateDeleteJobs(ctx context.Context, jobTenant tenant.Tenant, toDelete []*job.Spec, logWriter writer.LogWriter) error { + me := errors.NewMultiError("delete job specs check errors") + toDeleteMap := job.Specs(toDelete).ToFullNameAndSpecMap(jobTenant.ProjectName()) + + for _, jobToDelete := range toDelete { + downstreams, err := j.getAllDownstreams(ctx, jobTenant.ProjectName(), jobToDelete.Name(), map[job.FullName]bool{}) + if err != nil { + j.logger.Error("error getting all downstreams for job [%s]: %s", jobToDelete.Name().String(), err) + logWriter.Write(writer.LogLevelError, fmt.Sprintf("[%s] pre-delete check for job %s failed: %s", jobTenant.NamespaceName().String(), jobToDelete.Name().String(), err.Error())) + me.Append(err) + continue + } + validateDeleteJob(jobTenant, downstreams, toDeleteMap, jobToDelete, logWriter, me) + } + return me.ToErr() +} + +func validateDeleteJob(jobTenant tenant.Tenant, downstreams []*job.Downstream, toDeleteMap map[job.FullName]*job.Spec, jobToDelete *job.Spec, logWriter writer.LogWriter, me *errors.MultiError) bool { + notDeleted, safeToDelete := isJobSafeToDelete(toDeleteMap, job.DownstreamList(downstreams).GetDownstreamFullNames()) - // populate all jobs in project - jobsInProjectWithUpstreams, err := j.getJobsInProjectWithUpstreams(ctx, jobTenant.ProjectName()) + if !safeToDelete { + // TODO: refactor to put the log writer outside + errorMsg := fmt.Sprintf("deletion of job %s will fail. job is being used by %s", jobToDelete.Name().String(), job.FullNames(notDeleted).String()) + logWriter.Write(writer.LogLevelError, fmt.Sprintf("[%s] %s", jobTenant.NamespaceName().String(), errorMsg)) + me.Append(errors.NewError(errors.ErrFailedPrecond, job.EntityJob, errorMsg)) + return false + } + + return true +} + +func isJobSafeToDelete(toDeleteMap map[job.FullName]*job.Spec, downstreamFullNames []job.FullName) ([]job.FullName, bool) { + notDeleted := []job.FullName{} + for _, downstreamFullName := range downstreamFullNames { + if _, ok := toDeleteMap[downstreamFullName]; !ok { + notDeleted = append(notDeleted, downstreamFullName) + } + } + + return notDeleted, len(notDeleted) == 0 +} + +func (j *JobService) getAllDownstreams(ctx context.Context, projectName tenant.ProjectName, jobName job.Name, visited map[job.FullName]bool) ([]*job.Downstream, error) { + currentJobFullName := job.FullNameFrom(projectName, jobName) + downstreams := []*job.Downstream{} + visited[currentJobFullName] = true + childJobs, err := j.downstreamRepo.GetDownstreamByJobName(ctx, projectName, jobName) if err != nil { - return err + j.logger.Error("error getting downstream jobs for job [%s]: %s", jobName, err) + return nil, err } + for _, childJob := range childJobs { + downstreams = append(downstreams, childJob) + if visited[childJob.FullName()] { + continue + } + childDownstreams, err := j.getAllDownstreams(ctx, childJob.ProjectName(), childJob.Name(), visited) + if err != nil { + j.logger.Error("error getting all downstreams for job [%s]: %s", childJob.Name(), err) + return nil, err + } + downstreams = append(downstreams, childDownstreams...) + } + return downstreams, nil +} - jobMap := make(map[job.Name]*job.WithUpstream) - identifierToJobsMap := make(map[string][]*job.WithUpstream) - for _, jobEntity := range append(jobsWithUnresolvedUpstreams, jobsInProjectWithUpstreams...) { - jobSpecName := jobEntity.Job().Spec().Name() - if _, ok := jobMap[jobSpecName]; ok { +func getAllJobsToValidateMap(incomingJobs, existingJobs []*job.Job, unmodifiedSpecs []*job.Spec) map[job.Name]*job.WithUpstream { + // TODO: check whether we need to accumulate encountered errors + me := errors.NewMultiError("validate specs errors") + + existingJobMap := job.Jobs(existingJobs).GetNameAndJobMap() + var unmodifiedJobs []*job.Job + for _, unmodifiedSpec := range unmodifiedSpecs { + if unmodifiedJob, ok := existingJobMap[unmodifiedSpec.Name()]; ok { + unmodifiedJobs = append(unmodifiedJobs, unmodifiedJob) continue } - jobMap[jobSpecName] = jobEntity + errorsMsg := fmt.Sprintf("unable to validate existing job %s", unmodifiedSpec.Name().String()) + me.Append(errors.NewError(errors.ErrInternalError, job.EntityJob, errorsMsg)) + } + jobsToValidateMap := make(map[job.Name]*job.WithUpstream) + for _, jobToValidate := range append(incomingJobs, unmodifiedJobs...) { + jobWithUpstream, err := jobToValidate.GetJobWithUnresolvedUpstream() + if err != nil { + me.Append(err) + continue + } + jobsToValidateMap[jobToValidate.Spec().Name()] = jobWithUpstream + } + return jobsToValidateMap +} + +func getIdentifierToJobsMap(jobsToValidateMap map[job.Name]*job.WithUpstream) map[string][]*job.WithUpstream { + identifierToJobsMap := make(map[string][]*job.WithUpstream) + for _, jobEntity := range jobsToValidateMap { jobIdentifiers := []string{jobEntity.Job().FullName()} if jobDestination := jobEntity.Job().Destination().String(); jobDestination != "" { jobIdentifiers = append(jobIdentifiers, jobDestination) @@ -358,20 +691,7 @@ func (j *JobService) Validate(ctx context.Context, jobTenant tenant.Tenant, jobS identifierToJobsMap[jobIdentifier] = append(identifierToJobsMap[jobIdentifier], jobEntity) } } - - // check cyclic deps for every job - for _, jobEntity := range jobsWithUnresolvedUpstreams { - if _, err := j.validateCyclic(jobEntity.Job().Spec().Name(), jobMap, identifierToJobsMap); err != nil { - me.Append(err) - break - } - } - - if len(me.Errors) > 0 { - return me - } - - return nil + return identifierToJobsMap } func (j *JobService) resolveAndSaveUpstreams(ctx context.Context, jobTenant tenant.Tenant, logWriter writer.LogWriter, jobsToResolve ...[]*job.Job) error { @@ -380,6 +700,7 @@ func (j *JobService) resolveAndSaveUpstreams(ctx context.Context, jobTenant tena allJobsToResolve = append(allJobsToResolve, group...) } if len(allJobsToResolve) == 0 { + j.logger.Warn("no jobs to be resolved") return nil } @@ -390,107 +711,149 @@ func (j *JobService) resolveAndSaveUpstreams(ctx context.Context, jobTenant tena me.Append(err) j.logger.Debug("replacing upstreams for %d jobs of project [%s] namespace [%s]", len(jobsWithUpstreams), jobTenant.ProjectName(), jobTenant.NamespaceName()) - err = j.repo.ReplaceUpstreams(ctx, jobsWithUpstreams) + err = j.upstreamRepo.ReplaceUpstreams(ctx, jobsWithUpstreams) me.Append(err) - return errors.MultiToError(me) + return me.ToErr() } func (j *JobService) bulkAdd(ctx context.Context, tenantWithDetails *tenant.WithDetails, specsToAdd []*job.Spec, logWriter writer.LogWriter) ([]*job.Job, error) { - j.logger.Debug("adding %d jobs to project [%s] namespace [%s]", len(specsToAdd), tenantWithDetails.Project().Name(), tenantWithDetails.Namespace().Name()) me := errors.NewMultiError("bulk add specs errors") jobsToAdd, err := j.generateJobs(ctx, tenantWithDetails, specsToAdd, logWriter) me.Append(err) if len(jobsToAdd) == 0 { - return nil, errors.MultiToError(me) + j.logger.Warn("no jobs to be added") + return nil, me.ToErr() } // TODO: consider do add inside parallel - addedJobs, err := j.repo.Add(ctx, jobsToAdd) + addedJobs, err := j.jobRepo.Add(ctx, jobsToAdd) if err != nil { + j.logger.Error("error adding jobs for namespace [%s]: %s", tenantWithDetails.Namespace().Name(), err) logWriter.Write(writer.LogLevelError, fmt.Sprintf("[%s] add jobs failure found: %s", tenantWithDetails.Namespace().Name().String(), err.Error())) me.Append(err) } if len(addedJobs) > 0 { logWriter.Write(writer.LogLevelDebug, fmt.Sprintf("[%s] successfully added %d jobs", tenantWithDetails.Namespace().Name().String(), len(addedJobs))) + for _, job := range addedJobs { + j.raiseCreateEvent(job) + } + raiseJobEventMetric(tenantWithDetails.ToTenant(), job.MetricJobEventStateAdded, len(addedJobs)) } - return addedJobs, errors.MultiToError(me) + return addedJobs, me.ToErr() } func (j *JobService) bulkUpdate(ctx context.Context, tenantWithDetails *tenant.WithDetails, specsToUpdate []*job.Spec, logWriter writer.LogWriter) ([]*job.Job, error) { - j.logger.Debug("updating %d jobs of project [%s] namespace [%s]", len(specsToUpdate), tenantWithDetails.Project().Name(), tenantWithDetails.Namespace().Name()) me := errors.NewMultiError("bulk update specs errors") jobsToUpdate, err := j.generateJobs(ctx, tenantWithDetails, specsToUpdate, logWriter) me.Append(err) if len(jobsToUpdate) == 0 { - return nil, errors.MultiToError(me) + j.logger.Warn("no jobs to be updated") + return nil, me.ToErr() } - updatedJobs, err := j.repo.Update(ctx, jobsToUpdate) + updatedJobs, err := j.jobRepo.Update(ctx, jobsToUpdate) if err != nil { + j.logger.Error("error updating jobs for namespace [%s]: %s", tenantWithDetails.Namespace().Name(), err) logWriter.Write(writer.LogLevelError, fmt.Sprintf("[%s] update jobs failure found: %s", tenantWithDetails.Namespace().Name().String(), err.Error())) me.Append(err) } if len(updatedJobs) > 0 { logWriter.Write(writer.LogLevelDebug, fmt.Sprintf("[%s] successfully updated %d jobs", tenantWithDetails.Namespace().Name().String(), len(updatedJobs))) + for _, job := range updatedJobs { + j.raiseUpdateEvent(job) + } + raiseJobEventMetric(tenantWithDetails.ToTenant(), job.MetricJobEventStateUpdated, len(updatedJobs)) } - return updatedJobs, errors.MultiToError(me) + return updatedJobs, me.ToErr() } -func (j *JobService) bulkDelete(ctx context.Context, jobTenant tenant.Tenant, toDelete []*job.Spec, logWriter writer.LogWriter) error { - j.logger.Debug("deleting %d jobs of project [%s] namespace [%s]", len(toDelete), jobTenant.ProjectName(), jobTenant.NamespaceName()) +func (j *JobService) bulkDelete(ctx context.Context, jobTenant tenant.Tenant, toDelete []*job.Spec, logWriter writer.LogWriter) ([]job.Name, error) { me := errors.NewMultiError("bulk delete specs errors") - deletedJob := 0 + var deletedJobNames []job.Name + toDeleteMap := job.Specs(toDelete).ToFullNameAndSpecMap(jobTenant.ProjectName()) + + alreadyDeleted := map[job.FullName]bool{} for _, spec := range toDelete { // TODO: reuse Delete method and pass forceFlag as false - downstreamList, err := j.repo.GetDownstreamByJobName(ctx, jobTenant.ProjectName(), spec.Name()) + fullName := job.FullNameFrom(jobTenant.ProjectName(), spec.Name()) + downstreams, err := j.getAllDownstreams(ctx, jobTenant.ProjectName(), spec.Name(), map[job.FullName]bool{}) if err != nil { + j.logger.Error("error getting downstreams for job [%s]: %s", spec.Name(), err) logWriter.Write(writer.LogLevelError, fmt.Sprintf("[%s] pre-delete check for job %s failed: %s", jobTenant.NamespaceName().String(), spec.Name().String(), err.Error())) me.Append(err) continue } - if len(downstreamList) > 0 { - downstreamFullNames := job.DownstreamList(downstreamList).GetDownstreamFullNames() - errorMsg := fmt.Sprintf("deleting job %s failed. job is being used by %s", spec.Name().String(), downstreamFullNames.String()) - logWriter.Write(writer.LogLevelError, fmt.Sprintf("[%s] %s", jobTenant.NamespaceName().String(), errorMsg)) - me.Append(errors.NewError(errors.ErrFailedPrecond, job.EntityJob, errorMsg)) + isSafeToDelete := validateDeleteJob(jobTenant, downstreams, toDeleteMap, spec, logWriter, me) + if !isSafeToDelete { + j.logger.Warn("job [%s] is not safe to be deleted", spec.Name()) continue } logWriter.Write(writer.LogLevelDebug, fmt.Sprintf("[%s] deleting job %s", jobTenant.NamespaceName().String(), spec.Name().String())) - if err = j.repo.Delete(ctx, jobTenant.ProjectName(), spec.Name(), false); err != nil { + isDeletionFail := false + for i := len(downstreams) - 1; i >= 0 && !isDeletionFail; i-- { + if alreadyDeleted[downstreams[i].FullName()] { + continue + } + if err = j.jobRepo.Delete(ctx, downstreams[i].ProjectName(), downstreams[i].Name(), false); err != nil { + j.logger.Error("error deleting [%s] as downstream of [%s]", downstreams[i].Name(), spec.Name()) + logWriter.Write(writer.LogLevelError, fmt.Sprintf("[%s] deleting job %s failed: %s", downstreams[i].NamespaceName().String(), downstreams[i].Name().String(), err.Error())) + me.Append(err) + isDeletionFail = true + } else { + alreadyDeleted[downstreams[i].FullName()] = true + j.raiseDeleteEvent(jobTenant, spec.Name()) + raiseJobEventMetric(jobTenant, job.MetricJobEventStateDeleted, 1) + deletedJobNames = append(deletedJobNames, downstreams[i].Name()) + } + } + + if alreadyDeleted[fullName] || isDeletionFail { + j.logger.Warn("job [%s] deletion is skipped [already deleted or failure in deleting downstreams]", spec.Name()) + continue + } + if err = j.jobRepo.Delete(ctx, jobTenant.ProjectName(), spec.Name(), false); err != nil { + j.logger.Error("error deleting job [%s]", spec.Name()) logWriter.Write(writer.LogLevelError, fmt.Sprintf("[%s] deleting job %s failed: %s", jobTenant.NamespaceName().String(), spec.Name().String(), err.Error())) me.Append(err) } else { - deletedJob++ + alreadyDeleted[fullName] = true + j.raiseDeleteEvent(jobTenant, spec.Name()) + raiseJobEventMetric(jobTenant, job.MetricJobEventStateDeleted, 1) + deletedJobNames = append(deletedJobNames, spec.Name()) } } - if deletedJob > 0 { - logWriter.Write(writer.LogLevelDebug, fmt.Sprintf("[%s] successfully deleted %d jobs", jobTenant.NamespaceName().String(), deletedJob)) + + if len(deletedJobNames) > 0 { + logWriter.Write(writer.LogLevelDebug, fmt.Sprintf("[%s] successfully deleted %d jobs", jobTenant.NamespaceName().String(), len(deletedJobNames))) + } + + if len(deletedJobNames) < len(toDelete) { + totalFailed := len(toDelete) - len(deletedJobNames) + raiseJobEventMetric(jobTenant, job.MetricJobEventStateDeleteFailed, totalFailed) } - return errors.MultiToError(me) + return deletedJobNames, me.ToErr() } -func (j *JobService) differentiateSpecs(ctx context.Context, jobTenant tenant.Tenant, specs []*job.Spec, jobNamesWithValidationError []job.Name) (added, modified, deleted []*job.Spec, err error) { +func (*JobService) differentiateSpecs(existingJobs []*job.Job, specs []*job.Spec, jobNamesWithInvalidSpec []job.Name) (added, modified, deleted, unmodified []*job.Spec, err error) { + // TODO: consider checking multi-error if it is required here me := errors.NewMultiError("differentiate specs errors") - existingJobs, err := j.repo.GetAllByTenant(ctx, jobTenant) - me.Append(err) - - var addedSpecs, modifiedSpecs, deletedSpecs []*job.Spec + var addedSpecs, modifiedSpecs, unmodifiedSpecs, deletedSpecs []*job.Spec existingSpecsMap := job.Jobs(existingJobs).GetNameAndSpecMap() - for _, jobNameToSkip := range jobNamesWithValidationError { + for _, jobNameToSkip := range jobNamesWithInvalidSpec { delete(existingSpecsMap, jobNameToSkip) } @@ -499,12 +862,10 @@ func (j *JobService) differentiateSpecs(ctx context.Context, jobTenant tenant.Te addedSpecs = append(addedSpecs, incomingSpec) } else if !reflect.DeepEqual(spec, incomingSpec) { modifiedSpecs = append(modifiedSpecs, incomingSpec) + } else { + unmodifiedSpecs = append(unmodifiedSpecs, incomingSpec) } } - telemetry.NewCounter("total_jobs_modified", map[string]string{ - "project": jobTenant.ProjectName().String(), - "namespace": jobTenant.NamespaceName().String(), - }).Add(float64(len(modifiedSpecs))) incomingSpecsMap := job.Specs(specs).ToNameAndSpecMap() for existingJobName, existingJobSpec := range existingSpecsMap { @@ -512,7 +873,7 @@ func (j *JobService) differentiateSpecs(ctx context.Context, jobTenant tenant.Te deletedSpecs = append(deletedSpecs, existingJobSpec) } } - return addedSpecs, modifiedSpecs, deletedSpecs, errors.MultiToError(me) + return addedSpecs, modifiedSpecs, deletedSpecs, unmodifiedSpecs, me.ToErr() } func (j *JobService) generateJobs(ctx context.Context, tenantWithDetails *tenant.WithDetails, specs []*job.Spec, logWriter writer.LogWriter) ([]*job.Job, error) { @@ -524,6 +885,7 @@ func (j *JobService) generateJobs(ctx context.Context, tenantWithDetails *tenant return func() (interface{}, error) { generatedJob, err := j.generateJob(ctx, tenantWithDetails, currentSpec) if err != nil { + j.logger.Error("error generating job [%s]: %s", currentSpec.Name(), err) lw.Write(writer.LogLevelError, fmt.Sprintf("[%s] unable to generate job %s: %s", tenantWithDetails.Namespace().Name().String(), currentSpec.Name().String(), err.Error())) return nil, err } @@ -542,18 +904,20 @@ func (j *JobService) generateJobs(ctx context.Context, tenantWithDetails *tenant generatedJobs = append(generatedJobs, specVal) } } - return generatedJobs, errors.MultiToError(me) + return generatedJobs, me.ToErr() } func (j *JobService) generateJob(ctx context.Context, tenantWithDetails *tenant.WithDetails, spec *job.Spec) (*job.Job, error) { destination, err := j.pluginService.GenerateDestination(ctx, tenantWithDetails, spec.Task()) if err != nil && !errors.Is(err, ErrUpstreamModNotFound) { + j.logger.Error("error generating destination for [%s]: %s", spec.Name(), err) errorMsg := fmt.Sprintf("unable to add %s: %s", spec.Name().String(), err.Error()) return nil, errors.NewError(errors.ErrInternalError, job.EntityJob, errorMsg) } sources, err := j.pluginService.GenerateUpstreams(ctx, tenantWithDetails, spec, true) if err != nil && !errors.Is(err, ErrUpstreamModNotFound) { + j.logger.Error("error generating upstream for [%s]: %s", spec.Name(), err) errorMsg := fmt.Sprintf("unable to add %s: %s", spec.Name().String(), err.Error()) return nil, errors.NewError(errors.ErrInternalError, job.EntityJob, errorMsg) } @@ -561,15 +925,6 @@ func (j *JobService) generateJob(ctx context.Context, tenantWithDetails *tenant. return job.NewJob(tenantWithDetails.ToTenant(), spec, destination, sources), nil } -func (j *JobService) getJobsInProjectWithUpstreams(ctx context.Context, projectName tenant.ProjectName) ([]*job.WithUpstream, error) { - jobsInProject, err := j.GetByFilter(ctx, filter.WithString(filter.ProjectName, projectName.String())) - if err != nil { - return nil, err - } - - return job.Jobs(jobsInProject).GetJobsWithUnresolvedUpstreams() -} - func (j *JobService) validateCyclic(rootName job.Name, jobMap map[job.Name]*job.WithUpstream, identifierToJobMap map[string][]*job.WithUpstream) ([]string, error) { dagTree := j.buildDAGTree(rootName, jobMap, identifierToJobMap) return dagTree.ValidateCyclic() @@ -610,7 +965,7 @@ func (*JobService) buildDAGTree(rootName job.Name, jobMap map[job.Name]*job.With return dagTree } -// sources: https://github.com/odpf/optimus/blob/a6dafbc1fbeb8e1f1eb8d4a6e9582ada4a7f639e/job/replay.go#L101 +// sources: https://github.com/raystack/optimus/blob/a6dafbc1fbeb8e1f1eb8d4a6e9582ada4a7f639e/job/replay.go#L101 func findOrCreateDAGNode(dagTree *tree.MultiRootTree, dag tree.TreeData) *tree.TreeNode { node, ok := dagTree.GetNodeByName(dag.GetName()) if !ok { @@ -627,41 +982,43 @@ func (j *JobService) GetJobBasicInfo(ctx context.Context, jobTenant tenant.Tenan if spec != nil { tenantWithDetails, err := j.tenantDetailsGetter.GetDetails(ctx, jobTenant) if err != nil { + j.logger.Info("error getting tenant details: %s", err) logger.Write(writer.LogLevelError, fmt.Sprintf("unable to get tenant detail, err: %v", err)) return nil, logger } subjectJob, err = j.generateJob(ctx, tenantWithDetails, spec) if err != nil { + j.logger.Info("error generating job for [%s]: %s", spec.Name(), err) logger.Write(writer.LogLevelError, fmt.Sprintf("unable to generate job, err: %v", err)) return nil, logger } } else { subjectJob, err = j.Get(ctx, jobTenant, jobName) if err != nil { + j.logger.Info("error getting job [%s]: %s", jobName, err) logger.Write(writer.LogLevelError, fmt.Sprintf("unable to get job, err: %v", err)) return nil, logger } } if len(subjectJob.Sources()) == 0 { + j.logger.Warn("no job sources detected") logger.Write(writer.LogLevelInfo, "no job sources detected") } - if subjectJob.Spec().Schedule().CatchUp() { - logger.Write(writer.LogLevelWarning, "catchup is enabled") - } - if dupDestJobNames, err := j.getJobNamesWithSameDestination(ctx, subjectJob); err != nil { logger.Write(writer.LogLevelError, "could not perform duplicate job destination check, err: "+err.Error()) } else if dupDestJobNames != "" { logger.Write(writer.LogLevelWarning, "job already exists with same Destination: "+subjectJob.Destination().String()+" existing jobNames: "+dupDestJobNames) } + return subjectJob, logger } func (j *JobService) getJobNamesWithSameDestination(ctx context.Context, subjectJob *job.Job) (string, error) { - sameDestinationJobs, err := j.repo.GetAllByResourceDestination(ctx, subjectJob.Destination()) + sameDestinationJobs, err := j.jobRepo.GetAllByResourceDestination(ctx, subjectJob.Destination()) if err != nil { + j.logger.Error("error getting all jobs by destination [%s]: %s", subjectJob.Destination(), err) return "", err } var jobNames []string @@ -679,12 +1036,64 @@ func (j *JobService) GetUpstreamsToInspect(ctx context.Context, subjectJob *job. if localJob { return j.upstreamResolver.Resolve(ctx, subjectJob, logWriter) } - return j.repo.GetUpstreams(ctx, subjectJob.ProjectName(), subjectJob.Spec().Name()) + return j.upstreamRepo.GetUpstreams(ctx, subjectJob.ProjectName(), subjectJob.Spec().Name()) } func (j *JobService) GetDownstream(ctx context.Context, subjectJob *job.Job, localJob bool) ([]*job.Downstream, error) { if localJob { - return j.repo.GetDownstreamByDestination(ctx, subjectJob.ProjectName(), subjectJob.Destination()) + return j.downstreamRepo.GetDownstreamByDestination(ctx, subjectJob.ProjectName(), subjectJob.Destination()) } - return j.repo.GetDownstreamByJobName(ctx, subjectJob.ProjectName(), subjectJob.Spec().Name()) + return j.downstreamRepo.GetDownstreamByJobName(ctx, subjectJob.ProjectName(), subjectJob.Spec().Name()) +} + +func (j *JobService) raiseCreateEvent(job *job.Job) { + jobEvent, err := event.NewJobCreatedEvent(job) + if err != nil { + j.logger.Error("error creating event for job create: %s", err) + return + } + j.eventHandler.HandleEvent(jobEvent) +} + +func (j *JobService) raiseUpdateEvent(job *job.Job) { + jobEvent, err := event.NewJobUpdateEvent(job) + if err != nil { + j.logger.Error("error creating event for job update: %s", err) + return + } + j.eventHandler.HandleEvent(jobEvent) +} + +func (j *JobService) raiseStateChangeEvent(tnnt tenant.Tenant, jobName job.Name, state job.State) { + jobEvent, err := event.NewJobStateChangeEvent(tnnt, jobName, state) + if err != nil { + j.logger.Error("error creating event for job state change: %s", err) + return + } + j.eventHandler.HandleEvent(jobEvent) +} + +func (j *JobService) raiseDeleteEvent(tnnt tenant.Tenant, jobName job.Name) { + jobEvent, err := event.NewJobDeleteEvent(tnnt, jobName) + if err != nil { + j.logger.Error("error creating event for job delete: %s", err) + return + } + j.eventHandler.HandleEvent(jobEvent) +} + +func (*JobService) groupDownstreamPerProject(downstreams []*job.Downstream) map[tenant.ProjectName][]*job.Downstream { + output := make(map[tenant.ProjectName][]*job.Downstream) + for _, d := range downstreams { + output[d.ProjectName()] = append(output[d.ProjectName()], d) + } + return output +} + +func raiseJobEventMetric(jobTenant tenant.Tenant, state string, metricValue int) { + telemetry.NewCounter(job.MetricJobEvent, map[string]string{ + "project": jobTenant.ProjectName().String(), + "namespace": jobTenant.NamespaceName().String(), + "status": state, + }).Add(float64(metricValue)) } diff --git a/core/job/service/job_service_test.go b/core/job/service/job_service_test.go index 4acc5e202c..ffa0c42d68 100644 --- a/core/job/service/job_service_test.go +++ b/core/job/service/job_service_test.go @@ -3,20 +3,22 @@ package service_test import ( "context" "errors" + "fmt" "testing" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/core/job" - "github.com/odpf/optimus/core/job/service" - "github.com/odpf/optimus/core/job/service/filter" - "github.com/odpf/optimus/core/tenant" - optErrors "github.com/odpf/optimus/internal/errors" - "github.com/odpf/optimus/internal/models" - "github.com/odpf/optimus/internal/writer" - "github.com/odpf/optimus/sdk/plugin" + "github.com/raystack/optimus/core/event/moderator" + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/core/job/service" + "github.com/raystack/optimus/core/job/service/filter" + "github.com/raystack/optimus/core/tenant" + optErrors "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/models" + "github.com/raystack/optimus/internal/writer" + "github.com/raystack/optimus/sdk/plugin" ) func TestJobService(t *testing.T) { @@ -59,13 +61,20 @@ func TestJobService(t *testing.T) { log := log.NewNoop() - var jobNamesWithValidationError []job.Name + var jobNamesWithInvalidSpec []job.Name + var emptyJobNames []string t.Run("Add", func(t *testing.T) { t.Run("add jobs", func(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -75,6 +84,11 @@ func TestJobService(t *testing.T) { tenantDetailsGetter := new(TenantDetailsGetter) defer tenantDetailsGetter.AssertExpectations(t) + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() specs := []*job.Spec{specA} @@ -94,9 +108,14 @@ func TestJobService(t *testing.T) { jobWithUpstream := job.NewWithUpstream(jobA, []*job.Upstream{upstream}) upstreamResolver.On("BulkResolve", ctx, project.Name(), jobs, mock.Anything).Return([]*job.WithUpstream{jobWithUpstream}, nil, nil) - jobRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) + upstreamRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) + + jobNamesToUpload := []string{jobA.GetName()} + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, jobNamesToUpload, emptyJobNames).Return(nil) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + eventHandler.On("HandleEvent", mock.Anything).Times(1) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) err := jobService.Add(ctx, sampleTenant, specs) assert.NoError(t, err) }) @@ -104,6 +123,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -118,7 +143,7 @@ func TestJobService(t *testing.T) { tenantDetailsGetter.On("GetDetails", ctx, sampleTenant).Return(&tenant.WithDetails{}, errors.New("internal error")) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) err := jobService.Add(ctx, sampleTenant, specs) assert.ErrorContains(t, err, "internal error") }) @@ -126,6 +151,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -135,6 +166,11 @@ func TestJobService(t *testing.T) { tenantDetailsGetter := new(TenantDetailsGetter) defer tenantDetailsGetter.AssertExpectations(t) + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() specB, _ := job.NewSpecBuilder(jobVersion, "job-B", "sample-owner", jobSchedule, jobWindow, jobTask).Build() specC, _ := job.NewSpecBuilder(jobVersion, "job-C", "sample-owner", jobSchedule, jobWindow, jobTask).Build() @@ -161,9 +197,14 @@ func TestJobService(t *testing.T) { jobWithUpstream := job.NewWithUpstream(jobA, []*job.Upstream{upstream}) upstreamResolver.On("BulkResolve", ctx, project.Name(), jobs, mock.Anything).Return([]*job.WithUpstream{jobWithUpstream}, nil, nil) - jobRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) + upstreamRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) + + jobNamesToUpload := []string{jobA.GetName()} + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, jobNamesToUpload, emptyJobNames).Return(nil) + + eventHandler.On("HandleEvent", mock.Anything).Times(1) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) err := jobService.Add(ctx, sampleTenant, specs) assert.ErrorContains(t, err, "generate upstream error") }) @@ -171,6 +212,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -188,7 +235,7 @@ func TestJobService(t *testing.T) { upstreamResolver.On("BulkResolve", ctx, mock.Anything, mock.Anything, mock.Anything).Return(nil, nil) - jobRepo.On("ReplaceUpstreams", ctx, mock.Anything).Return(nil) + upstreamRepo.On("ReplaceUpstreams", ctx, mock.Anything).Return(nil) tenantDetailsGetter.On("GetDetails", ctx, sampleTenant).Return(detailedTenant, nil) @@ -199,7 +246,7 @@ func TestJobService(t *testing.T) { pluginService.On("GenerateUpstreams", ctx, detailedTenant, mock.Anything, true).Return(nil, errors.New("generate upstream error")) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) err := jobService.Add(ctx, sampleTenant, specs) assert.ErrorContains(t, err, "generate upstream error") }) @@ -207,6 +254,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -216,6 +269,11 @@ func TestJobService(t *testing.T) { tenantDetailsGetter := new(TenantDetailsGetter) defer tenantDetailsGetter.AssertExpectations(t) + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() specs := []*job.Spec{specA} @@ -232,9 +290,14 @@ func TestJobService(t *testing.T) { jobWithUpstream := job.NewWithUpstream(jobA, nil) upstreamResolver.On("BulkResolve", ctx, project.Name(), jobs, mock.Anything).Return([]*job.WithUpstream{jobWithUpstream}, nil, nil) - jobRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) + upstreamRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) + + jobNamesToUpload := []string{jobA.GetName()} + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, jobNamesToUpload, emptyJobNames).Return(nil) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + eventHandler.On("HandleEvent", mock.Anything).Times(1) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) err := jobService.Add(ctx, sampleTenant, specs) assert.NoError(t, err) }) @@ -242,6 +305,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -251,6 +320,11 @@ func TestJobService(t *testing.T) { tenantDetailsGetter := new(TenantDetailsGetter) defer tenantDetailsGetter.AssertExpectations(t) + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() specB, _ := job.NewSpecBuilder(jobVersion, "job-B", "sample-owner", jobSchedule, jobWindow, jobTask).Build() specs := []*job.Spec{specA, specB} @@ -268,14 +342,19 @@ func TestJobService(t *testing.T) { jobB := job.NewJob(sampleTenant, specB, "", nil) savedJobs := []*job.Job{jobB} - jobRepo.On("Add", ctx, mock.Anything).Return(savedJobs, errors.New("unable to save job A"), nil) + jobRepo.On("Add", ctx, mock.Anything).Return(savedJobs, errors.New("unable to save job A")) jobWithUpstreamB := job.NewWithUpstream(jobB, nil) upstreamResolver.On("BulkResolve", ctx, project.Name(), savedJobs, mock.Anything).Return([]*job.WithUpstream{jobWithUpstreamB}, nil, nil) - jobRepo.On("ReplaceUpstreams", ctx, mock.Anything).Return(nil) + upstreamRepo.On("ReplaceUpstreams", ctx, mock.Anything).Return(nil) + + jobNamesToUpload := []string{jobB.GetName()} + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, jobNamesToUpload, emptyJobNames).Return(nil) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + eventHandler.On("HandleEvent", mock.Anything).Times(1) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) err := jobService.Add(ctx, sampleTenant, specs) assert.ErrorContains(t, err, "unable to save job A") }) @@ -283,6 +362,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -299,7 +384,7 @@ func TestJobService(t *testing.T) { upstreamResolver.On("BulkResolve", ctx, mock.Anything, mock.Anything, mock.Anything).Return(nil, nil) - jobRepo.On("ReplaceUpstreams", ctx, mock.Anything).Return(nil) + upstreamRepo.On("ReplaceUpstreams", ctx, mock.Anything).Return(nil) resourceA := job.ResourceURN("resource-A") pluginService.On("GenerateDestination", ctx, detailedTenant, specA.Task()).Return(resourceA, nil).Once() @@ -309,7 +394,7 @@ func TestJobService(t *testing.T) { jobRepo.On("Add", ctx, mock.Anything).Return([]*job.Job{}, errors.New("unable to save job A"), errors.New("all jobs failed")) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) err := jobService.Add(ctx, sampleTenant, specs) assert.ErrorContains(t, err, "unable to save job A") }) @@ -317,6 +402,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -326,6 +417,11 @@ func TestJobService(t *testing.T) { tenantDetailsGetter := new(TenantDetailsGetter) defer tenantDetailsGetter.AssertExpectations(t) + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() specs := []*job.Spec{specA} @@ -345,18 +441,83 @@ func TestJobService(t *testing.T) { jobWithUpstreamA := job.NewWithUpstream(jobA, nil) upstreamResolver.On("BulkResolve", ctx, project.Name(), jobs, mock.Anything).Return([]*job.WithUpstream{jobWithUpstreamA}, nil, nil) - jobRepo.On("ReplaceUpstreams", ctx, mock.Anything).Return(errors.New("internal error")) + upstreamRepo.On("ReplaceUpstreams", ctx, mock.Anything).Return(errors.New("internal error")) + + jobNamesToUpload := []string{jobA.GetName()} + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, jobNamesToUpload, emptyJobNames).Return(nil) + + eventHandler.On("HandleEvent", mock.Anything).Times(1) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) err := jobService.Add(ctx, sampleTenant, specs) assert.Error(t, err) }) + t.Run("return error if encounter issue when uploading jobs", func(t *testing.T) { + jobRepo := new(JobRepository) + defer jobRepo.AssertExpectations(t) + + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + + pluginService := new(PluginService) + defer pluginService.AssertExpectations(t) + + upstreamResolver := new(UpstreamResolver) + defer upstreamResolver.AssertExpectations(t) + + tenantDetailsGetter := new(TenantDetailsGetter) + defer tenantDetailsGetter.AssertExpectations(t) + + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + specs := []*job.Spec{specA} + + tenantDetailsGetter.On("GetDetails", ctx, sampleTenant).Return(detailedTenant, nil) + + jobADestination := job.ResourceURN("resource-A") + pluginService.On("GenerateDestination", ctx, detailedTenant, specA.Task()).Return(jobADestination, nil) + + jobAUpstreamName := []job.ResourceURN{"job-B"} + pluginService.On("GenerateUpstreams", ctx, detailedTenant, specA, true).Return(jobAUpstreamName, nil) + + jobA := job.NewJob(sampleTenant, specA, jobADestination, jobAUpstreamName) + jobs := []*job.Job{jobA} + jobRepo.On("Add", ctx, mock.Anything).Return(jobs, nil, nil) + + upstream := job.NewUpstreamResolved("job-B", "", "resource-B", sampleTenant, "static", taskName, false) + jobWithUpstream := job.NewWithUpstream(jobA, []*job.Upstream{upstream}) + upstreamResolver.On("BulkResolve", ctx, project.Name(), jobs, mock.Anything).Return([]*job.WithUpstream{jobWithUpstream}, nil, nil) + + upstreamRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) + + errorMsg := "internal error" + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, mock.Anything, emptyJobNames).Return(errors.New(errorMsg)) + + eventHandler.On("HandleEvent", mock.Anything).Times(1) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) + err := jobService.Add(ctx, sampleTenant, specs) + assert.ErrorContains(t, err, errorMsg) + }) }) t.Run("Update", func(t *testing.T) { t.Run("update jobs", func(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -366,6 +527,11 @@ func TestJobService(t *testing.T) { tenantDetailsGetter := new(TenantDetailsGetter) defer tenantDetailsGetter.AssertExpectations(t) + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() specs := []*job.Spec{specA} @@ -385,9 +551,14 @@ func TestJobService(t *testing.T) { jobWithUpstream := job.NewWithUpstream(jobA, []*job.Upstream{upstream}) upstreamResolver.On("BulkResolve", ctx, project.Name(), jobs, mock.Anything).Return([]*job.WithUpstream{jobWithUpstream}, nil, nil) - jobRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) + upstreamRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobNamesToUpload := []string{jobA.GetName()} + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, jobNamesToUpload, emptyJobNames).Return(nil) + + eventHandler.On("HandleEvent", mock.Anything).Times(1) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) err := jobService.Update(ctx, sampleTenant, specs) assert.NoError(t, err) }) @@ -395,6 +566,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -409,7 +586,7 @@ func TestJobService(t *testing.T) { tenantDetailsGetter.On("GetDetails", ctx, sampleTenant).Return(&tenant.WithDetails{}, errors.New("internal error")) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) err := jobService.Update(ctx, sampleTenant, specs) assert.ErrorContains(t, err, "internal error") }) @@ -417,6 +594,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -426,6 +609,11 @@ func TestJobService(t *testing.T) { tenantDetailsGetter := new(TenantDetailsGetter) defer tenantDetailsGetter.AssertExpectations(t) + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() specB, _ := job.NewSpecBuilder(jobVersion, "job-B", "sample-owner", jobSchedule, jobWindow, jobTask).Build() specC, _ := job.NewSpecBuilder(jobVersion, "job-C", "sample-owner", jobSchedule, jobWindow, jobTask).Build() @@ -452,9 +640,14 @@ func TestJobService(t *testing.T) { jobWithUpstream := job.NewWithUpstream(jobA, []*job.Upstream{upstream}) upstreamResolver.On("BulkResolve", ctx, project.Name(), jobs, mock.Anything).Return([]*job.WithUpstream{jobWithUpstream}, nil, nil) - jobRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) + upstreamRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) + + jobNamesToUpload := []string{jobA.GetName()} + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, jobNamesToUpload, emptyJobNames).Return(nil) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + eventHandler.On("HandleEvent", mock.Anything).Times(1) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) err := jobService.Update(ctx, sampleTenant, specs) assert.ErrorContains(t, err, "generate upstream error") }) @@ -462,6 +655,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -479,7 +678,7 @@ func TestJobService(t *testing.T) { upstreamResolver.On("BulkResolve", ctx, mock.Anything, mock.Anything, mock.Anything).Return(nil, nil) - jobRepo.On("ReplaceUpstreams", ctx, mock.Anything).Return(nil) + upstreamRepo.On("ReplaceUpstreams", ctx, mock.Anything).Return(nil) tenantDetailsGetter.On("GetDetails", ctx, sampleTenant).Return(detailedTenant, nil) @@ -490,7 +689,7 @@ func TestJobService(t *testing.T) { pluginService.On("GenerateUpstreams", ctx, detailedTenant, mock.Anything, true).Return(nil, errors.New("generate upstream error")) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) err := jobService.Update(ctx, sampleTenant, specs) assert.ErrorContains(t, err, "generate upstream error") }) @@ -498,6 +697,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -507,6 +712,11 @@ func TestJobService(t *testing.T) { tenantDetailsGetter := new(TenantDetailsGetter) defer tenantDetailsGetter.AssertExpectations(t) + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() specs := []*job.Spec{specA} @@ -523,9 +733,14 @@ func TestJobService(t *testing.T) { jobWithUpstream := job.NewWithUpstream(jobA, nil) upstreamResolver.On("BulkResolve", ctx, project.Name(), jobs, mock.Anything).Return([]*job.WithUpstream{jobWithUpstream}, nil, nil) - jobRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) + upstreamRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) + + jobNamesToUpload := []string{jobA.GetName()} + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, jobNamesToUpload, emptyJobNames).Return(nil) + + eventHandler.On("HandleEvent", mock.Anything).Times(1) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) err := jobService.Update(ctx, sampleTenant, specs) assert.NoError(t, err) }) @@ -533,6 +748,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -542,6 +763,11 @@ func TestJobService(t *testing.T) { tenantDetailsGetter := new(TenantDetailsGetter) defer tenantDetailsGetter.AssertExpectations(t) + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() specB, _ := job.NewSpecBuilder(jobVersion, "job-B", "sample-owner", jobSchedule, jobWindow, jobTask).Build() specs := []*job.Spec{specA, specB} @@ -564,9 +790,14 @@ func TestJobService(t *testing.T) { jobWithUpstreamB := job.NewWithUpstream(jobB, nil) upstreamResolver.On("BulkResolve", ctx, project.Name(), savedJobs, mock.Anything).Return([]*job.WithUpstream{jobWithUpstreamB}, nil, nil) - jobRepo.On("ReplaceUpstreams", ctx, mock.Anything).Return(nil) + upstreamRepo.On("ReplaceUpstreams", ctx, mock.Anything).Return(nil) + + jobNamesToUpload := []string{jobB.GetName()} + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, jobNamesToUpload, emptyJobNames).Return(nil) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + eventHandler.On("HandleEvent", mock.Anything).Times(1) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) err := jobService.Update(ctx, sampleTenant, specs) assert.ErrorContains(t, err, "unable to save job A") }) @@ -574,6 +805,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -590,7 +827,7 @@ func TestJobService(t *testing.T) { upstreamResolver.On("BulkResolve", ctx, mock.Anything, mock.Anything, mock.Anything).Return(nil, nil) - jobRepo.On("ReplaceUpstreams", ctx, mock.Anything).Return(nil) + upstreamRepo.On("ReplaceUpstreams", ctx, mock.Anything).Return(nil) resourceA := job.ResourceURN("resource-A") pluginService.On("GenerateDestination", ctx, detailedTenant, specA.Task()).Return(resourceA, nil).Once() @@ -600,7 +837,7 @@ func TestJobService(t *testing.T) { jobRepo.On("Update", ctx, mock.Anything).Return([]*job.Job{}, errors.New("unable to update job A"), errors.New("all jobs failed")) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) err := jobService.Update(ctx, sampleTenant, specs) assert.ErrorContains(t, err, "unable to update job A") }) @@ -608,6 +845,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -617,6 +860,11 @@ func TestJobService(t *testing.T) { tenantDetailsGetter := new(TenantDetailsGetter) defer tenantDetailsGetter.AssertExpectations(t) + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() specs := []*job.Spec{specA} @@ -635,32 +883,202 @@ func TestJobService(t *testing.T) { jobWithUpstreamA := job.NewWithUpstream(jobA, nil) upstreamResolver.On("BulkResolve", ctx, project.Name(), jobs, mock.Anything).Return([]*job.WithUpstream{jobWithUpstreamA}, nil, nil) - jobRepo.On("ReplaceUpstreams", ctx, mock.Anything).Return(errors.New("internal error")) + upstreamRepo.On("ReplaceUpstreams", ctx, mock.Anything).Return(errors.New("internal error")) + + jobNamesToUpload := []string{jobA.GetName()} + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, jobNamesToUpload, emptyJobNames).Return(nil) + + eventHandler.On("HandleEvent", mock.Anything).Times(1) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) err := jobService.Update(ctx, sampleTenant, specs) assert.Error(t, err) }) + t.Run("return error if encounter issue when uploading jobs", func(t *testing.T) { + jobRepo := new(JobRepository) + defer jobRepo.AssertExpectations(t) + + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + + pluginService := new(PluginService) + defer pluginService.AssertExpectations(t) + + upstreamResolver := new(UpstreamResolver) + defer upstreamResolver.AssertExpectations(t) + + tenantDetailsGetter := new(TenantDetailsGetter) + defer tenantDetailsGetter.AssertExpectations(t) + + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + specs := []*job.Spec{specA} + + tenantDetailsGetter.On("GetDetails", ctx, sampleTenant).Return(detailedTenant, nil) + + jobADestination := job.ResourceURN("resource-A") + pluginService.On("GenerateDestination", ctx, detailedTenant, specA.Task()).Return(jobADestination, nil) + + jobAUpstreamName := []job.ResourceURN{"job-B"} + pluginService.On("GenerateUpstreams", ctx, detailedTenant, specA, true).Return(jobAUpstreamName, nil) + + jobA := job.NewJob(sampleTenant, specA, jobADestination, jobAUpstreamName) + jobs := []*job.Job{jobA} + jobRepo.On("Update", ctx, mock.Anything).Return(jobs, nil, nil) + + upstream := job.NewUpstreamResolved("job-B", "", "resource-B", sampleTenant, "static", taskName, false) + jobWithUpstream := job.NewWithUpstream(jobA, []*job.Upstream{upstream}) + upstreamResolver.On("BulkResolve", ctx, project.Name(), jobs, mock.Anything).Return([]*job.WithUpstream{jobWithUpstream}, nil, nil) + + upstreamRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) + + errorMsg := "internal error" + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, mock.Anything, emptyJobNames).Return(errors.New(errorMsg)) + + eventHandler.On("HandleEvent", mock.Anything).Times(1) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) + err := jobService.Update(ctx, sampleTenant, specs) + assert.ErrorContains(t, err, errorMsg) + }) }) + + t.Run("ChangeNamespace", func(t *testing.T) { + newNamespaceName := "newNamespace" + newTenant, _ := tenant.NewTenant(project.Name().String(), newNamespaceName) + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + jobA := job.NewJob(newTenant, specA, "table-A", []job.ResourceURN{"table-B"}) + t.Run("should fail if error in repo", func(t *testing.T) { + jobRepo := new(JobRepository) + jobRepo.On("ChangeJobNamespace", ctx, specA.Name(), sampleTenant, newTenant).Return(errors.New("error in transaction")) + defer jobRepo.AssertExpectations(t) + + jobService := service.NewJobService(jobRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil) + err := jobService.ChangeNamespace(ctx, sampleTenant, newTenant, specA.Name()) + assert.ErrorContains(t, err, "error in transaction") + }) + + t.Run("should fail if error getting newly created job", func(t *testing.T) { + jobRepo := new(JobRepository) + jobRepo.On("ChangeJobNamespace", ctx, specA.Name(), sampleTenant, newTenant).Return(nil) + jobRepo.On("GetByJobName", ctx, project.Name(), specA.Name()).Return(nil, errors.New("error in fetching job from DB")) + defer jobRepo.AssertExpectations(t) + + jobService := service.NewJobService(jobRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil) + err := jobService.ChangeNamespace(ctx, sampleTenant, newTenant, specA.Name()) + assert.ErrorContains(t, err, "error in fetching job from DB") + }) + + t.Run("should fail if error in upload job", func(t *testing.T) { + jobRepo := new(JobRepository) + jobRepo.On("ChangeJobNamespace", ctx, specA.Name(), sampleTenant, newTenant).Return(nil) + jobRepo.On("GetByJobName", ctx, project.Name(), specA.Name()).Return(jobA, nil) + defer jobRepo.AssertExpectations(t) + + jobDeploymentService := new(JobDeploymentService) + jobModified := []string{specA.Name().String()} + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, emptyJobNames, jobModified).Return(errors.New("error in upload jobs")) + defer jobDeploymentService.AssertExpectations(t) + + jobService := service.NewJobService(jobRepo, nil, nil, nil, nil, nil, nil, nil, jobDeploymentService, nil) + err := jobService.ChangeNamespace(ctx, sampleTenant, newTenant, specA.Name()) + assert.ErrorContains(t, err, "error in upload jobs") + }) + + t.Run("should fail if error in upload new job", func(t *testing.T) { + jobRepo := new(JobRepository) + jobRepo.On("ChangeJobNamespace", ctx, specA.Name(), sampleTenant, newTenant).Return(nil) + jobRepo.On("GetByJobName", ctx, project.Name(), specA.Name()).Return(jobA, nil) + defer jobRepo.AssertExpectations(t) + + jobDeploymentService := new(JobDeploymentService) + jobModified := []string{specA.Name().String()} + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, emptyJobNames, jobModified).Return(nil) + jobDeploymentService.On("UploadJobs", ctx, newTenant, jobModified, emptyJobNames).Return(errors.New("error in upload new job")) + defer jobDeploymentService.AssertExpectations(t) + + jobService := service.NewJobService(jobRepo, nil, nil, nil, nil, nil, nil, nil, jobDeploymentService, nil) + + err := jobService.ChangeNamespace(ctx, sampleTenant, newTenant, specA.Name()) + assert.ErrorContains(t, err, "error in upload new job") + }) + + t.Run("successfully", func(t *testing.T) { + jobRepo := new(JobRepository) + jobRepo.On("ChangeJobNamespace", ctx, specA.Name(), sampleTenant, newTenant).Return(nil) + jobRepo.On("GetByJobName", ctx, project.Name(), specA.Name()).Return(jobA, nil) + defer jobRepo.AssertExpectations(t) + + jobDeploymentService := new(JobDeploymentService) + jobModified := []string{specA.Name().String()} + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, emptyJobNames, jobModified).Return(nil) + jobDeploymentService.On("UploadJobs", ctx, newTenant, jobModified, emptyJobNames).Return(nil) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + eventHandler.On("HandleEvent", mock.Anything).Times(1) + defer eventHandler.AssertExpectations(t) + + jobService := service.NewJobService(jobRepo, nil, nil, nil, nil, nil, eventHandler, nil, jobDeploymentService, nil) + err := jobService.ChangeNamespace(ctx, sampleTenant, newTenant, specA.Name()) + assert.NoError(t, err) + }) + }) + t.Run("Delete", func(t *testing.T) { t.Run("deletes job without downstream", func(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() - jobRepo.On("GetDownstreamByJobName", ctx, project.Name(), specA.Name()).Return(nil, nil) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), specA.Name()).Return(nil, nil) jobRepo.On("Delete", ctx, project.Name(), specA.Name(), false).Return(nil) - jobService := service.NewJobService(jobRepo, nil, nil, nil, nil) + jobNamesToRemove := []string{specA.Name().String()} + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, emptyJobNames, jobNamesToRemove).Return(nil) + + eventHandler.On("HandleEvent", mock.Anything).Times(1) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, nil, nil, nil, eventHandler, log, jobDeploymentService, nil) affectedDownstream, err := jobService.Delete(ctx, sampleTenant, specA.Name(), false, false) assert.NoError(t, err) assert.Empty(t, affectedDownstream) }) + t.Run("deletes job with downstream if it is a force delete", func(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() downstreamFullNames := []job.FullName{"test-proj/job-B", "test-proj/job-C"} @@ -669,10 +1087,15 @@ func TestJobService(t *testing.T) { job.NewDownstream("job-C", project.Name(), namespace.Name(), taskName), } - jobRepo.On("GetDownstreamByJobName", ctx, project.Name(), specA.Name()).Return(downstreamList, nil) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), specA.Name()).Return(downstreamList, nil) jobRepo.On("Delete", ctx, project.Name(), specA.Name(), false).Return(nil) - jobService := service.NewJobService(jobRepo, nil, nil, nil, nil) + jobNamesToRemove := []string{specA.Name().String()} + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, emptyJobNames, jobNamesToRemove).Return(nil) + + eventHandler.On("HandleEvent", mock.Anything).Times(1) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, nil, nil, nil, eventHandler, log, jobDeploymentService, nil) + affectedDownstream, err := jobService.Delete(ctx, sampleTenant, specA.Name(), false, true) assert.NoError(t, err) assert.EqualValues(t, downstreamFullNames, affectedDownstream) @@ -681,15 +1104,21 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() downstreamList := []*job.Downstream{ job.NewDownstream("job-B", project.Name(), namespace.Name(), taskName), job.NewDownstream("job-C", project.Name(), namespace.Name(), taskName), } - jobRepo.On("GetDownstreamByJobName", ctx, project.Name(), specA.Name()).Return(downstreamList, nil) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), specA.Name()).Return(downstreamList, nil) - jobService := service.NewJobService(jobRepo, nil, nil, nil, nil) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, nil, nil, nil, nil, log, nil, nil) affectedDownstream, err := jobService.Delete(ctx, sampleTenant, specA.Name(), false, false) assert.Error(t, err) assert.Empty(t, affectedDownstream) @@ -698,11 +1127,18 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() - jobRepo.On("GetDownstreamByJobName", ctx, project.Name(), specA.Name()).Return(nil, errors.New("internal error")) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), specA.Name()).Return(nil, errors.New("internal error")) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, nil, nil, nil, nil, log, nil, nil) - jobService := service.NewJobService(jobRepo, nil, nil, nil, nil) affectedDownstream, err := jobService.Delete(ctx, sampleTenant, specA.Name(), false, false) assert.Error(t, err) assert.Empty(t, affectedDownstream) @@ -711,22 +1147,62 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() - jobRepo.On("GetDownstreamByJobName", ctx, project.Name(), specA.Name()).Return(nil, nil) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), specA.Name()).Return(nil, nil) jobRepo.On("Delete", ctx, project.Name(), specA.Name(), false).Return(errors.New("internal error")) - jobService := service.NewJobService(jobRepo, nil, nil, nil, nil) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, nil, nil, nil, nil, log, nil, nil) affectedDownstream, err := jobService.Delete(ctx, sampleTenant, specA.Name(), false, false) assert.Error(t, err) assert.Empty(t, affectedDownstream) }) + t.Run("return error if encounter issue when removing jobs from scheduler", func(t *testing.T) { + jobRepo := new(JobRepository) + defer jobRepo.AssertExpectations(t) + + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), specA.Name()).Return(nil, nil) + jobRepo.On("Delete", ctx, project.Name(), specA.Name(), false).Return(nil) + + errorMsg := "internal error" + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, emptyJobNames, mock.Anything).Return(errors.New(errorMsg)) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, nil, nil, nil, eventHandler, log, jobDeploymentService, nil) + affectedDownstream, err := jobService.Delete(ctx, sampleTenant, specA.Name(), false, false) + assert.ErrorContains(t, err, errorMsg) + assert.Empty(t, affectedDownstream) + }) }) t.Run("ReplaceAll", func(t *testing.T) { t.Run("adds new jobs that does not exist yet", func(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -739,6 +1215,11 @@ func TestJobService(t *testing.T) { logWriter := new(mockWriter) defer logWriter.AssertExpectations(t) + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + tenantDetailsGetter.On("GetDetails", ctx, sampleTenant).Return(detailedTenant, nil) specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() @@ -765,18 +1246,29 @@ func TestJobService(t *testing.T) { jobWithUpstream := job.NewWithUpstream(jobA, []*job.Upstream{upstream}) upstreamResolver.On("BulkResolve", ctx, project.Name(), []*job.Job{jobA}, mock.Anything).Return([]*job.WithUpstream{jobWithUpstream}, nil, nil) - jobRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) + upstreamRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) logWriter.On("Write", mock.Anything, mock.Anything).Return(nil) + eventHandler.On("HandleEvent", mock.Anything).Times(1) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) - err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithValidationError, logWriter) + jobNamesToUpload := []string{jobA.GetName()} + var jobNamesToRemove []string + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, jobNamesToUpload, jobNamesToRemove).Return(nil) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) + err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithInvalidSpec, logWriter) assert.NoError(t, err) }) t.Run("updates modified existing jobs", func(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -789,6 +1281,11 @@ func TestJobService(t *testing.T) { logWriter := new(mockWriter) defer logWriter.AssertExpectations(t) + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + tenantDetailsGetter.On("GetDetails", ctx, sampleTenant).Return(detailedTenant, nil) specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() @@ -809,24 +1306,35 @@ func TestJobService(t *testing.T) { pluginService.On("GenerateUpstreams", ctx, detailedTenant, specA, true).Return(jobAUpstreamName, nil) jobRepo.On("Update", ctx, mock.Anything).Return([]*job.Job{jobA}, nil) + eventHandler.On("HandleEvent", mock.Anything).Times(1) upstream := job.NewUpstreamResolved("job-B", "", "resource-B", sampleTenant, "static", taskName, false) jobWithUpstream := job.NewWithUpstream(jobA, []*job.Upstream{upstream}) upstreamResolver.On("BulkResolve", ctx, project.Name(), []*job.Job{jobA}, mock.Anything).Return([]*job.WithUpstream{jobWithUpstream}, nil, nil) - jobRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) + upstreamRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) logWriter.On("Write", mock.Anything, mock.Anything).Return(nil) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) - err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithValidationError, logWriter) + jobNamesToUpload := []string{jobA.GetName()} + var jobNamesToRemove []string + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, jobNamesToUpload, jobNamesToRemove).Return(nil) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) + err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithInvalidSpec, logWriter) assert.NoError(t, err) }) t.Run("deletes the removed jobs", func(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -839,6 +1347,11 @@ func TestJobService(t *testing.T) { logWriter := new(mockWriter) defer logWriter.AssertExpectations(t) + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + tenantDetailsGetter.On("GetDetails", ctx, sampleTenant).Return(detailedTenant, nil) specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() @@ -853,19 +1366,96 @@ func TestJobService(t *testing.T) { jobRepo.On("GetAllByTenant", ctx, sampleTenant).Return(existingSpecs, nil) - jobRepo.On("GetDownstreamByJobName", ctx, project.Name(), specB.Name()).Return(nil, nil) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), specB.Name()).Return(nil, nil) jobRepo.On("Delete", ctx, project.Name(), specB.Name(), false).Return(nil) + eventHandler.On("HandleEvent", mock.Anything).Times(1) logWriter.On("Write", mock.Anything, mock.Anything).Return(nil) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) - err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithValidationError, logWriter) + var jobNamesToUpload []string + jobNamesToRemove := []string{specB.Name().String()} + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, jobNamesToUpload, jobNamesToRemove).Return(nil) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) + err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithInvalidSpec, logWriter) + assert.NoError(t, err) + }) + t.Run("deletes the jobs which the downstreams are also be deleted", func(t *testing.T) { + jobRepo := new(JobRepository) + defer jobRepo.AssertExpectations(t) + + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + + pluginService := new(PluginService) + defer pluginService.AssertExpectations(t) + + upstreamResolver := new(UpstreamResolver) + defer upstreamResolver.AssertExpectations(t) + + tenantDetailsGetter := new(TenantDetailsGetter) + defer tenantDetailsGetter.AssertExpectations(t) + + logWriter := new(mockWriter) + defer logWriter.AssertExpectations(t) + + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + + tenantDetailsGetter.On("GetDetails", ctx, sampleTenant).Return(detailedTenant, nil) + + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + specB, _ := job.NewSpecBuilder(jobVersion, "job-B", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + specC, _ := job.NewSpecBuilder(jobVersion, "job-C", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + specD, _ := job.NewSpecBuilder(jobVersion, "job-D", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + jobA := job.NewJob(sampleTenant, specA, "", nil) + jobB := job.NewJob(sampleTenant, specB, "", nil) + jobC := job.NewJob(sampleTenant, specC, "", nil) + jobD := job.NewJob(sampleTenant, specD, "", nil) + + incomingSpecs := []*job.Spec{} + + existingSpecs := []*job.Job{jobA, jobB, jobC, jobD} + + jobRepo.On("GetAllByTenant", ctx, sampleTenant).Return(existingSpecs, nil) + + downstreamA := []*job.Downstream{ + job.NewDownstream("job-B", project.Name(), namespace.Name(), taskName), + job.NewDownstream("job-D", project.Name(), namespace.Name(), taskName), + } + downstreamB := []*job.Downstream{ + job.NewDownstream("job-C", project.Name(), namespace.Name(), taskName), + } + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), specA.Name()).Return(downstreamA, nil) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), specB.Name()).Return(downstreamB, nil) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), specC.Name()).Return(nil, nil) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), specD.Name()).Return(nil, nil) + jobRepo.On("Delete", ctx, project.Name(), mock.Anything, false).Return(nil) + eventHandler.On("HandleEvent", mock.Anything).Times(4) + + logWriter.On("Write", mock.Anything, mock.Anything).Return(nil) + + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, mock.Anything, mock.Anything).Return(nil) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) + err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithInvalidSpec, logWriter) assert.NoError(t, err) }) t.Run("adds, updates, and deletes jobs in a request", func(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -878,6 +1468,11 @@ func TestJobService(t *testing.T) { logWriter := new(mockWriter) defer logWriter.AssertExpectations(t) + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + tenantDetailsGetter.On("GetDetails", ctx, sampleTenant).Return(detailedTenant, nil) specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() @@ -910,25 +1505,36 @@ func TestJobService(t *testing.T) { jobB := job.NewJob(sampleTenant, specB, "", nil) jobRepo.On("Update", ctx, mock.Anything).Return([]*job.Job{jobB}, nil) - jobRepo.On("GetDownstreamByJobName", ctx, project.Name(), existingSpecC.Name()).Return(nil, nil) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), existingSpecC.Name()).Return(nil, nil) jobRepo.On("Delete", ctx, project.Name(), existingSpecC.Name(), false).Return(nil) upstream := job.NewUpstreamResolved("job-B", "", "resource-B", sampleTenant, "static", taskName, false) jobWithUpstream := job.NewWithUpstream(jobA, []*job.Upstream{upstream}) upstreamResolver.On("BulkResolve", ctx, project.Name(), []*job.Job{jobA, jobB}, mock.Anything).Return([]*job.WithUpstream{jobWithUpstream}, nil, nil) - jobRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) + upstreamRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) logWriter.On("Write", mock.Anything, mock.Anything).Return(nil) + eventHandler.On("HandleEvent", mock.Anything).Times(3) + + jobNamesToUpload := []string{jobA.GetName(), jobB.GetName()} + jobNamesToRemove := []string{existingJobC.GetName()} + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, jobNamesToUpload, jobNamesToRemove).Return(nil) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) - err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithValidationError, logWriter) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) + err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithInvalidSpec, logWriter) assert.NoError(t, err) }) t.Run("skips invalid job when classifying specs as added, modified, or deleted", func(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -941,6 +1547,11 @@ func TestJobService(t *testing.T) { logWriter := new(mockWriter) defer logWriter.AssertExpectations(t) + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + tenantDetailsGetter.On("GetDetails", ctx, sampleTenant).Return(detailedTenant, nil) specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() @@ -975,18 +1586,23 @@ func TestJobService(t *testing.T) { jobB := job.NewJob(sampleTenant, specB, "", nil) jobRepo.On("Update", ctx, mock.Anything).Return([]*job.Job{jobB}, nil) - jobRepo.On("GetDownstreamByJobName", ctx, project.Name(), existingSpecC.Name()).Return(nil, nil) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), existingSpecC.Name()).Return(nil, nil) jobRepo.On("Delete", ctx, project.Name(), existingSpecC.Name(), false).Return(nil) upstream := job.NewUpstreamResolved("job-B", "", "resource-B", sampleTenant, "static", taskName, false) jobWithUpstream := job.NewWithUpstream(jobA, []*job.Upstream{upstream}) upstreamResolver.On("BulkResolve", ctx, project.Name(), []*job.Job{jobA, jobB}, mock.Anything).Return([]*job.WithUpstream{jobWithUpstream}, nil, nil) - jobRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) + upstreamRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) logWriter.On("Write", mock.Anything, mock.Anything).Return(nil) + eventHandler.On("HandleEvent", mock.Anything).Times(3) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobNamesToUpload := []string{jobA.GetName(), jobB.GetName()} + jobNamesToRemove := []string{existingJobC.GetName()} + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, jobNamesToUpload, jobNamesToRemove).Return(nil) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, []job.Name{"job-D"}, logWriter) assert.NoError(t, err) }) @@ -994,6 +1610,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -1006,6 +1628,11 @@ func TestJobService(t *testing.T) { logWriter := new(mockWriter) defer logWriter.AssertExpectations(t) + eventHandler := newEventHandler(t) + + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + tenantDetailsGetter.On("GetDetails", ctx, sampleTenant).Return(detailedTenant, nil) specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() @@ -1020,19 +1647,30 @@ func TestJobService(t *testing.T) { var specADestination job.ResourceURN pluginService.On("GenerateDestination", ctx, detailedTenant, specA.Task()).Return(specADestination, errors.New("internal error")).Once() - jobRepo.On("GetDownstreamByJobName", ctx, project.Name(), existingSpecC.Name()).Return(nil, nil) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), existingSpecC.Name()).Return(nil, nil) jobRepo.On("Delete", ctx, project.Name(), existingSpecC.Name(), false).Return(nil) + eventHandler.On("HandleEvent", mock.Anything).Times(1) logWriter.On("Write", mock.Anything, mock.Anything).Return(nil) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) - err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithValidationError, logWriter) + var jobNamesToUpload []string + jobNamesToRemove := []string{existingJobC.GetName()} + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, jobNamesToUpload, jobNamesToRemove).Return(nil) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) + err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithInvalidSpec, logWriter) assert.ErrorContains(t, err, "internal error") }) t.Run("skips invalid modified jobs", func(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -1045,6 +1683,11 @@ func TestJobService(t *testing.T) { logWriter := new(mockWriter) defer logWriter.AssertExpectations(t) + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + tenantDetailsGetter.On("GetDetails", ctx, sampleTenant).Return(detailedTenant, nil) specB, _ := job.NewSpecBuilder(jobVersion, "job-B", "sample-owner", jobSchedule, jobWindow, jobTask).Build() @@ -1062,19 +1705,30 @@ func TestJobService(t *testing.T) { var jobBDestination job.ResourceURN pluginService.On("GenerateDestination", ctx, detailedTenant, specB.Task()).Return(jobBDestination, errors.New("internal error")).Once() - jobRepo.On("GetDownstreamByJobName", ctx, project.Name(), existingSpecC.Name()).Return(nil, nil) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), existingSpecC.Name()).Return(nil, nil) jobRepo.On("Delete", ctx, project.Name(), existingSpecC.Name(), false).Return(nil) + eventHandler.On("HandleEvent", mock.Anything).Times(1) logWriter.On("Write", mock.Anything, mock.Anything).Return(nil) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) - err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithValidationError, logWriter) + var jobNamesToUpload []string + jobNamesToRemove := []string{existingJobC.GetName()} + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, jobNamesToUpload, jobNamesToRemove).Return(nil) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) + err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithInvalidSpec, logWriter) assert.ErrorContains(t, err, "internal error") }) - t.Run("skips to delete jobs with downstream", func(t *testing.T) { + t.Run("skips to delete jobs if the downstream is not deleted", func(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -1087,16 +1741,24 @@ func TestJobService(t *testing.T) { logWriter := new(mockWriter) defer logWriter.AssertExpectations(t) + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + tenantDetailsGetter.On("GetDetails", ctx, sampleTenant).Return(detailedTenant, nil) specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() - incomingSpecs := []*job.Spec{specA} + specE, _ := job.NewSpecBuilder(jobVersion, "job-E", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + incomingSpecs := []*job.Spec{specA, specE} existingSpecC, _ := job.NewSpecBuilder(jobVersion, "job-C", "sample-owner", jobSchedule, jobWindow, jobTask).Build() existingJobC := job.NewJob(sampleTenant, existingSpecC, "", nil) existingSpecD, _ := job.NewSpecBuilder(jobVersion, "job-D", "sample-owner", jobSchedule, jobWindow, jobTask).Build() existingJobD := job.NewJob(sampleTenant, existingSpecD, "", nil) - existingSpecs := []*job.Job{existingJobC, existingJobD} + existingSpecE, _ := job.NewSpecBuilder(jobVersion, "job-E", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + existingJobE := job.NewJob(sampleTenant, existingSpecE, "", nil) + existingSpecs := []*job.Job{existingJobC, existingJobD, existingJobE} jobRepo.On("GetAllByTenant", ctx, sampleTenant).Return(existingSpecs, nil) @@ -1110,29 +1772,41 @@ func TestJobService(t *testing.T) { jobRepo.On("Add", ctx, mock.Anything).Return([]*job.Job{jobA}, nil) downstreamList := []*job.Downstream{ - job.NewDownstream("sample-job-E", project.Name(), namespace.Name(), taskName), + job.NewDownstream("job-E", project.Name(), namespace.Name(), taskName), } - jobRepo.On("GetDownstreamByJobName", ctx, project.Name(), existingSpecC.Name()).Return(downstreamList, nil) - jobRepo.On("GetDownstreamByJobName", ctx, project.Name(), existingSpecD.Name()).Return(nil, nil) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), existingSpecC.Name()).Return(downstreamList, nil) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), existingSpecD.Name()).Return(nil, nil) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), existingSpecE.Name()).Return(nil, nil) jobRepo.On("Delete", ctx, project.Name(), existingSpecD.Name(), false).Return(nil) upstream := job.NewUpstreamResolved("job-B", "", "resource-B", sampleTenant, "static", taskName, false) jobWithUpstream := job.NewWithUpstream(jobA, []*job.Upstream{upstream}) upstreamResolver.On("BulkResolve", ctx, project.Name(), []*job.Job{jobA}, mock.Anything).Return([]*job.WithUpstream{jobWithUpstream}, nil, nil) - jobRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) + upstreamRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) logWriter.On("Write", mock.Anything, mock.Anything).Return(nil) + eventHandler.On("HandleEvent", mock.Anything).Times(2) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) - err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithValidationError, logWriter) + jobNamesToUpload := []string{jobA.GetName()} + jobNamesToRemove := []string{existingJobD.GetName()} + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, jobNamesToUpload, jobNamesToRemove).Return(nil) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) + err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithInvalidSpec, logWriter) assert.ErrorContains(t, err, "job is being used by") }) t.Run("should not break process if one of job failed to be added", func(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -1145,6 +1819,11 @@ func TestJobService(t *testing.T) { logWriter := new(mockWriter) defer logWriter.AssertExpectations(t) + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + tenantDetailsGetter.On("GetDetails", ctx, sampleTenant).Return(detailedTenant, nil) specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() @@ -1173,18 +1852,29 @@ func TestJobService(t *testing.T) { jobWithUpstream := job.NewWithUpstream(jobA, []*job.Upstream{upstream}) upstreamResolver.On("BulkResolve", ctx, project.Name(), []*job.Job{jobA}, mock.Anything).Return([]*job.WithUpstream{jobWithUpstream}, nil, nil) - jobRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) + upstreamRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) logWriter.On("Write", mock.Anything, mock.Anything).Return(nil) + eventHandler.On("HandleEvent", mock.Anything).Times(1) + + jobNamesToUpload := []string{jobA.GetName()} + var jobNamesToRemove []string + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, jobNamesToUpload, jobNamesToRemove).Return(nil) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) - err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithValidationError, logWriter) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) + err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithInvalidSpec, logWriter) assert.ErrorContains(t, err, "internal error") }) t.Run("should not break process if one of job failed to be updated", func(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -1197,6 +1887,9 @@ func TestJobService(t *testing.T) { logWriter := new(mockWriter) defer logWriter.AssertExpectations(t) + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + tenantDetailsGetter.On("GetDetails", ctx, sampleTenant).Return(detailedTenant, nil) specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() @@ -1218,15 +1911,20 @@ func TestJobService(t *testing.T) { jobRepo.On("Update", ctx, mock.Anything).Return([]*job.Job{}, errors.New("internal error")) logWriter.On("Write", mock.Anything, mock.Anything).Return(nil) - - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) - err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithValidationError, logWriter) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, jobDeploymentService, nil) + err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithInvalidSpec, logWriter) assert.ErrorContains(t, err, "internal error") }) t.Run("should not break process if one of job failed to be deleted", func(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -1239,6 +1937,9 @@ func TestJobService(t *testing.T) { logWriter := new(mockWriter) defer logWriter.AssertExpectations(t) + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + tenantDetailsGetter.On("GetDetails", ctx, sampleTenant).Return(detailedTenant, nil) specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() @@ -1253,19 +1954,25 @@ func TestJobService(t *testing.T) { jobRepo.On("GetAllByTenant", ctx, sampleTenant).Return(existingSpecs, nil) - jobRepo.On("GetDownstreamByJobName", ctx, project.Name(), specB.Name()).Return(nil, nil) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), specB.Name()).Return(nil, nil) jobRepo.On("Delete", ctx, project.Name(), specB.Name(), false).Return(errors.New("internal error")) logWriter.On("Write", mock.Anything, mock.Anything).Return(nil).Times(3) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) - err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithValidationError, logWriter) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, jobDeploymentService, nil) + err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithInvalidSpec, logWriter) assert.ErrorContains(t, err, "internal error") }) t.Run("should not delete job if unable to check downstream of the job", func(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -1278,6 +1985,9 @@ func TestJobService(t *testing.T) { logWriter := new(mockWriter) defer logWriter.AssertExpectations(t) + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + tenantDetailsGetter.On("GetDetails", ctx, sampleTenant).Return(detailedTenant, nil) specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() @@ -1292,18 +2002,93 @@ func TestJobService(t *testing.T) { jobRepo.On("GetAllByTenant", ctx, sampleTenant).Return(existingSpecs, nil) - jobRepo.On("GetDownstreamByJobName", ctx, project.Name(), specB.Name()).Return(nil, errors.New("internal error")) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), specB.Name()).Return(nil, errors.New("internal error")) logWriter.On("Write", mock.Anything, mock.Anything).Return(nil).Twice() - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) - err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithValidationError, logWriter) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, jobDeploymentService, nil) + err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithInvalidSpec, logWriter) assert.ErrorContains(t, err, "internal error") }) + t.Run("should not delete job if one of its downstream is unable to delete", func(t *testing.T) { + jobRepo := new(JobRepository) + defer jobRepo.AssertExpectations(t) + + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + + pluginService := new(PluginService) + defer pluginService.AssertExpectations(t) + + upstreamResolver := new(UpstreamResolver) + defer upstreamResolver.AssertExpectations(t) + + tenantDetailsGetter := new(TenantDetailsGetter) + defer tenantDetailsGetter.AssertExpectations(t) + + logWriter := new(mockWriter) + defer logWriter.AssertExpectations(t) + + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + + tenantDetailsGetter.On("GetDetails", ctx, sampleTenant).Return(detailedTenant, nil) + + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + specB, _ := job.NewSpecBuilder(jobVersion, "job-B", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + specC, _ := job.NewSpecBuilder(jobVersion, "job-C", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + specD, _ := job.NewSpecBuilder(jobVersion, "job-D", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + jobA := job.NewJob(sampleTenant, specA, "", nil) + jobB := job.NewJob(sampleTenant, specB, "", nil) + jobC := job.NewJob(sampleTenant, specC, "", nil) + jobD := job.NewJob(sampleTenant, specD, "", nil) + + incomingSpecs := []*job.Spec{} + + existingSpecs := []*job.Job{jobA, jobB, jobC, jobD} + + jobRepo.On("GetAllByTenant", ctx, sampleTenant).Return(existingSpecs, nil) + + downstreamA := []*job.Downstream{ + job.NewDownstream("job-B", project.Name(), namespace.Name(), taskName), + job.NewDownstream("job-D", project.Name(), namespace.Name(), taskName), + } + downstreamB := []*job.Downstream{ + job.NewDownstream("job-C", project.Name(), namespace.Name(), taskName), + } + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), specA.Name()).Return(downstreamA, nil) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), specB.Name()).Return(downstreamB, nil) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), specC.Name()).Return(nil, nil) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), specD.Name()).Return(nil, nil) + jobRepo.On("Delete", ctx, project.Name(), specC.Name(), false).Return(errors.New("db error")) + jobRepo.On("Delete", ctx, project.Name(), specD.Name(), false).Return(nil) + eventHandler.On("HandleEvent", mock.Anything).Times(1) + + logWriter.On("Write", mock.Anything, mock.Anything).Return(nil) + + var jobNamesToUpload []string + jobNamesToRemove := []string{specD.Name().String()} + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, jobNamesToUpload, jobNamesToRemove).Return(nil) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) + err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithInvalidSpec, logWriter) + assert.Error(t, err) + }) t.Run("returns error if unable to get tenant details", func(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -1316,6 +2101,9 @@ func TestJobService(t *testing.T) { logWriter := new(mockWriter) defer logWriter.AssertExpectations(t) + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() specB, _ := job.NewSpecBuilder(jobVersion, "job-B", "sample-owner", jobSchedule, jobWindow, jobTask).Build() jobB := job.NewJob(sampleTenant, specB, "", nil) @@ -1330,8 +2118,74 @@ func TestJobService(t *testing.T) { errorMsg := "project/namespace error" tenantDetailsGetter.On("GetDetails", ctx, sampleTenant).Return(nil, errors.New(errorMsg)) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) - err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithValidationError, logWriter) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, jobDeploymentService, nil) + + err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithInvalidSpec, logWriter) + assert.ErrorContains(t, err, errorMsg) + }) + t.Run("returns error if encounter error when uploading/removing jobs", func(t *testing.T) { + jobRepo := new(JobRepository) + defer jobRepo.AssertExpectations(t) + + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + + pluginService := new(PluginService) + defer pluginService.AssertExpectations(t) + + upstreamResolver := new(UpstreamResolver) + defer upstreamResolver.AssertExpectations(t) + + tenantDetailsGetter := new(TenantDetailsGetter) + defer tenantDetailsGetter.AssertExpectations(t) + + logWriter := new(mockWriter) + defer logWriter.AssertExpectations(t) + + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + + tenantDetailsGetter.On("GetDetails", ctx, sampleTenant).Return(detailedTenant, nil) + + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + jobADestination := job.ResourceURN("resource-A") + jobAUpstreamName := []job.ResourceURN{"job-B"} + jobA := job.NewJob(sampleTenant, specA, jobADestination, jobAUpstreamName) + + specB, _ := job.NewSpecBuilder(jobVersion, "job-B", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + jobB := job.NewJob(sampleTenant, specB, "", nil) + + incomingSpecs := []*job.Spec{specA, specB} + + existingJobs := []*job.Job{jobB} + + jobRepo.On("GetAllByTenant", ctx, sampleTenant).Return(existingJobs, nil) + + pluginService.On("GenerateDestination", ctx, detailedTenant, specA.Task()).Return(jobADestination, nil).Once() + pluginService.On("GenerateUpstreams", ctx, detailedTenant, specA, true).Return(jobAUpstreamName, nil) + + jobRepo.On("Add", ctx, mock.Anything).Return([]*job.Job{jobA}, nil) + + upstream := job.NewUpstreamResolved("job-B", "", "resource-B", sampleTenant, "static", taskName, false) + + jobWithUpstream := job.NewWithUpstream(jobA, []*job.Upstream{upstream}) + upstreamResolver.On("BulkResolve", ctx, project.Name(), []*job.Job{jobA}, mock.Anything).Return([]*job.WithUpstream{jobWithUpstream}, nil, nil) + + upstreamRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobWithUpstream}).Return(nil) + + logWriter.On("Write", mock.Anything, mock.Anything).Return(nil) + eventHandler.On("HandleEvent", mock.Anything).Times(1) + + errorMsg := "internal error" + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, mock.Anything, mock.Anything).Return(errors.New(errorMsg)) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) + err := jobService.ReplaceAll(ctx, sampleTenant, incomingSpecs, jobNamesWithInvalidSpec, logWriter) assert.ErrorContains(t, err, errorMsg) }) }) @@ -1341,6 +2195,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -1353,6 +2213,11 @@ func TestJobService(t *testing.T) { logWriter := new(mockWriter) defer logWriter.AssertExpectations(t) + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() jobADestination := job.ResourceURN("resource-A") jobAUpstreamName := []job.ResourceURN{"job-B"} @@ -1381,11 +2246,16 @@ func TestJobService(t *testing.T) { jobBWithUpstream := job.NewWithUpstream(jobB, []*job.Upstream{upstreamC}) upstreamResolver.On("BulkResolve", ctx, project.Name(), []*job.Job{jobA, jobB}, mock.Anything).Return([]*job.WithUpstream{jobAWithUpstream, jobBWithUpstream}, nil) - jobRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobAWithUpstream, jobBWithUpstream}).Return(nil) + upstreamRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobAWithUpstream, jobBWithUpstream}).Return(nil) logWriter.On("Write", mock.Anything, mock.Anything).Return(nil).Times(3) + eventHandler.On("HandleEvent", mock.Anything).Times(2) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + var jobNamesToRemove []string + jobNamesToUpload := []string{jobA.GetName(), jobB.GetName()} + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, jobNamesToUpload, jobNamesToRemove).Return(nil) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) err := jobService.Refresh(ctx, project.Name(), []string{namespace.Name().String()}, nil, logWriter) assert.NoError(t, err) }) @@ -1393,6 +2263,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -1405,6 +2281,11 @@ func TestJobService(t *testing.T) { logWriter := new(mockWriter) defer logWriter.AssertExpectations(t) + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() jobADestination := job.ResourceURN("resource-A") jobAUpstreamName := []job.ResourceURN{"job-B"} @@ -1439,12 +2320,17 @@ func TestJobService(t *testing.T) { upstreamResolver.On("BulkResolve", ctx, project.Name(), []*job.Job{jobA}, mock.Anything).Return([]*job.WithUpstream{jobAWithUpstream}, nil) upstreamResolver.On("BulkResolve", ctx, project.Name(), []*job.Job{jobB}, mock.Anything).Return([]*job.WithUpstream{jobBWithUpstream}, nil) - jobRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobAWithUpstream}).Return(nil) - jobRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobBWithUpstream}).Return(nil) + upstreamRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobAWithUpstream}).Return(nil) + upstreamRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobBWithUpstream}).Return(nil) logWriter.On("Write", mock.Anything, mock.Anything).Return(nil).Times(4) + eventHandler.On("HandleEvent", mock.Anything).Times(2) + + var jobNamesToRemove []string + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, []string{jobA.GetName()}, jobNamesToRemove).Return(nil) + jobDeploymentService.On("UploadJobs", ctx, otherTenant, []string{jobB.GetName()}, jobNamesToRemove).Return(nil) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) err := jobService.Refresh(ctx, project.Name(), []string{namespace.Name().String(), otherNamespace.Name().String()}, nil, logWriter) assert.NoError(t, err) }) @@ -1452,6 +2338,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -1463,21 +2355,146 @@ func TestJobService(t *testing.T) { jobRepo.On("GetAllByTenant", ctx, sampleTenant).Return(nil, errors.New("internal error")) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) err := jobService.Refresh(ctx, project.Name(), []string{namespace.Name().String()}, nil, nil) assert.ErrorContains(t, err, "internal error") }) }) + t.Run("RefreshResourceDownstream", func(t *testing.T) { + t.Run("returns error if encountered error when identifying downstreams", func(t *testing.T) { + jobRepo := new(JobRepository) + defer jobRepo.AssertExpectations(t) + + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + + pluginService := new(PluginService) + defer pluginService.AssertExpectations(t) + + upstreamResolver := new(UpstreamResolver) + defer upstreamResolver.AssertExpectations(t) + + tenantDetailsGetter := new(TenantDetailsGetter) + defer tenantDetailsGetter.AssertExpectations(t) + + logWriter := new(mockWriter) + defer logWriter.AssertExpectations(t) + + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + + resourceURNs := []job.ResourceURN{"project.dataset.table"} + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) + + downstreamRepo.On("GetDownstreamBySources", ctx, resourceURNs).Return(nil, errors.New("internal error")) + + err := jobService.RefreshResourceDownstream(ctx, resourceURNs, logWriter) + assert.ErrorContains(t, err, "internal error") + }) + + t.Run("should refresh and return nil if no error is encountered when refreshing downstreams", func(t *testing.T) { + jobRepo := new(JobRepository) + defer jobRepo.AssertExpectations(t) + + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + + pluginService := new(PluginService) + defer pluginService.AssertExpectations(t) + + upstreamResolver := new(UpstreamResolver) + defer upstreamResolver.AssertExpectations(t) + + tenantDetailsGetter := new(TenantDetailsGetter) + defer tenantDetailsGetter.AssertExpectations(t) + + logWriter := new(mockWriter) + defer logWriter.AssertExpectations(t) + + jobDeploymentService := new(JobDeploymentService) + defer jobDeploymentService.AssertExpectations(t) + + eventHandler := newEventHandler(t) + + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + jobADestination := job.ResourceURN("resource-A") + jobAUpstreamName := job.ResourceURN("job-B") + jobA := job.NewJob(sampleTenant, specA, jobADestination, []job.ResourceURN{jobAUpstreamName}) + + specB, _ := job.NewSpecBuilder(jobVersion, "job-B", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + jobBDestination := job.ResourceURN("resource-B") + jobBUpstreamName := job.ResourceURN("job-C") + jobB := job.NewJob(sampleTenant, specB, jobBDestination, []job.ResourceURN{jobBUpstreamName}) + + resourceURNs := []job.ResourceURN{jobAUpstreamName, jobBUpstreamName} + + jobDownstreams := []*job.Downstream{ + job.NewDownstream(jobA.Spec().Name(), sampleTenant.ProjectName(), sampleTenant.NamespaceName(), jobTask.Name()), + job.NewDownstream(jobB.Spec().Name(), sampleTenant.ProjectName(), sampleTenant.NamespaceName(), jobTask.Name()), + } + + downstreamRepo.On("GetDownstreamBySources", ctx, resourceURNs).Return(jobDownstreams, nil) + jobRepo.On("GetByJobName", ctx, sampleTenant.ProjectName(), jobA.Spec().Name()).Return(jobA, nil) + jobRepo.On("GetByJobName", ctx, sampleTenant.ProjectName(), jobB.Spec().Name()).Return(jobB, nil) + + tenantDetailsGetter.On("GetDetails", ctx, sampleTenant).Return(detailedTenant, nil) + + pluginService.On("GenerateDestination", ctx, detailedTenant, specA.Task()).Return(jobADestination, nil).Once() + pluginService.On("GenerateUpstreams", ctx, detailedTenant, specA, true).Return([]job.ResourceURN{jobAUpstreamName}, nil) + + pluginService.On("GenerateDestination", ctx, detailedTenant, specB.Task()).Return(jobBDestination, nil).Once() + pluginService.On("GenerateUpstreams", ctx, detailedTenant, specB, true).Return([]job.ResourceURN{jobBUpstreamName}, nil) + + jobRepo.On("Update", ctx, mock.Anything).Return([]*job.Job{jobA, jobB}, nil) + + upstreamB := job.NewUpstreamResolved("job-B", "", "resource-B", sampleTenant, "static", taskName, false) + jobAWithUpstream := job.NewWithUpstream(jobA, []*job.Upstream{upstreamB}) + upstreamC := job.NewUpstreamResolved("job-C", "", "resource-C", sampleTenant, "static", taskName, false) + jobBWithUpstream := job.NewWithUpstream(jobB, []*job.Upstream{upstreamC}) + upstreamResolver.On("BulkResolve", ctx, project.Name(), []*job.Job{jobA, jobB}, mock.Anything).Return([]*job.WithUpstream{jobAWithUpstream, jobBWithUpstream}, nil) + + upstreamRepo.On("ReplaceUpstreams", ctx, []*job.WithUpstream{jobAWithUpstream, jobBWithUpstream}).Return(nil) + + logWriter.On("Write", mock.Anything, mock.Anything).Return(nil).Times(3) + eventHandler.On("HandleEvent", mock.Anything).Times(2) + + var jobNamesToRemove []string + jobNamesToUpload := []string{jobA.GetName(), jobB.GetName()} + jobDeploymentService.On("UploadJobs", ctx, sampleTenant, jobNamesToUpload, jobNamesToRemove).Return(nil) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, eventHandler, log, jobDeploymentService, nil) + + err := jobService.RefreshResourceDownstream(ctx, resourceURNs, logWriter) + assert.NoError(t, err) + }) + }) + t.Run("Get", func(t *testing.T) { t.Run("return error when repo get by job name error", func(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + jobName, _ := job.NameFrom("job-A") jobRepo.On("GetByJobName", ctx, sampleTenant.ProjectName(), jobName).Return(nil, errors.New("error when fetch job")) - jobService := service.NewJobService(jobRepo, nil, nil, nil, nil) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, nil, nil, nil, nil, log, nil, nil) + actual, err := jobService.Get(ctx, sampleTenant, jobName) assert.Error(t, err, "error when fetch job") assert.Nil(t, actual) @@ -1486,11 +2503,18 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() jobA := job.NewJob(sampleTenant, specA, "table-A", []job.ResourceURN{"table-B"}) jobRepo.On("GetByJobName", ctx, sampleTenant.ProjectName(), specA.Name()).Return(jobA, nil) - jobService := service.NewJobService(jobRepo, nil, nil, nil, nil) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, nil, nil, nil, nil, log, nil, nil) + actual, err := jobService.Get(ctx, sampleTenant, specA.Name()) assert.NoError(t, err, "error when fetch job") assert.NotNil(t, actual) @@ -1506,7 +2530,7 @@ func TestJobService(t *testing.T) { jobRepo.On("GetAllByResourceDestination", ctx, job.ResourceURN("example")).Return(nil, errors.New("error encountered")) - jobService := service.NewJobService(jobRepo, nil, nil, nil, nil) + jobService := service.NewJobService(jobRepo, nil, nil, nil, nil, nil, nil, log, nil, nil) actual, err := jobService.GetByFilter(ctx, filter.WithString(filter.ResourceDestination, "example")) assert.Error(t, err, "error encountered") assert.Nil(t, actual) @@ -1519,7 +2543,7 @@ func TestJobService(t *testing.T) { jobA := job.NewJob(sampleTenant, specA, "table-A", []job.ResourceURN{"table-B"}) jobRepo.On("GetAllByResourceDestination", ctx, job.ResourceURN("table-A")).Return([]*job.Job{jobA}, nil) - jobService := service.NewJobService(jobRepo, nil, nil, nil, nil) + jobService := service.NewJobService(jobRepo, nil, nil, nil, nil, nil, nil, log, nil, nil) actual, err := jobService.GetByFilter(ctx, filter.WithString(filter.ResourceDestination, "table-A")) assert.NoError(t, err) assert.NotNil(t, actual) @@ -1535,7 +2559,7 @@ func TestJobService(t *testing.T) { jobName, _ := job.NameFrom("job-A") jobRepo.On("GetByJobName", ctx, sampleTenant.ProjectName(), jobName).Return(nil, errors.New("error encountered")) - jobService := service.NewJobService(jobRepo, nil, nil, nil, nil) + jobService := service.NewJobService(jobRepo, nil, nil, nil, nil, nil, nil, log, nil, nil) actual, err := jobService.GetByFilter(ctx, filter.WithString(filter.ProjectName, sampleTenant.ProjectName().String()), filter.WithStringArray(filter.JobNames, []string{jobName.String()}), @@ -1553,7 +2577,7 @@ func TestJobService(t *testing.T) { jobRepo.On("GetByJobName", ctx, sampleTenant.ProjectName(), specA.Name()).Return(jobA, nil) jobRepo.On("GetByJobName", ctx, sampleTenant.ProjectName(), specB.Name()).Return(nil, errors.New("error encountered")) - jobService := service.NewJobService(jobRepo, nil, nil, nil, nil) + jobService := service.NewJobService(jobRepo, nil, nil, nil, nil, nil, nil, log, nil, nil) actual, err := jobService.GetByFilter(ctx, filter.WithString(filter.ProjectName, sampleTenant.ProjectName().String()), filter.WithStringArray(filter.JobNames, []string{specA.Name().String(), specB.Name().String()}), @@ -1570,7 +2594,7 @@ func TestJobService(t *testing.T) { specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() jobRepo.On("GetByJobName", ctx, sampleTenant.ProjectName(), specA.Name()).Return(nil, optErrors.NotFound(job.EntityJob, "job not found")) - jobService := service.NewJobService(jobRepo, nil, nil, nil, nil) + jobService := service.NewJobService(jobRepo, nil, nil, nil, nil, nil, nil, log, nil, nil) actual, err := jobService.GetByFilter(ctx, filter.WithString(filter.ProjectName, sampleTenant.ProjectName().String()), filter.WithStringArray(filter.JobNames, []string{specA.Name().String()}), @@ -1586,7 +2610,7 @@ func TestJobService(t *testing.T) { jobA := job.NewJob(sampleTenant, specA, "table-A", []job.ResourceURN{"table-B"}) jobRepo.On("GetByJobName", ctx, sampleTenant.ProjectName(), specA.Name()).Return(jobA, nil) - jobService := service.NewJobService(jobRepo, nil, nil, nil, nil) + jobService := service.NewJobService(jobRepo, nil, nil, nil, nil, nil, nil, log, nil, nil) actual, err := jobService.GetByFilter(ctx, filter.WithString(filter.ProjectName, sampleTenant.ProjectName().String()), filter.WithStringArray(filter.JobNames, []string{specA.Name().String()}), @@ -1605,7 +2629,7 @@ func TestJobService(t *testing.T) { jobName, _ := job.NameFrom("job-A") jobRepo.On("GetByJobName", ctx, sampleTenant.ProjectName(), jobName).Return(nil, errors.New("error encountered")) - jobService := service.NewJobService(jobRepo, nil, nil, nil, nil) + jobService := service.NewJobService(jobRepo, nil, nil, nil, nil, nil, nil, log, nil, nil) actual, err := jobService.GetByFilter(ctx, filter.WithString(filter.ProjectName, sampleTenant.ProjectName().String()), filter.WithString(filter.JobName, jobName.String()), @@ -1620,7 +2644,7 @@ func TestJobService(t *testing.T) { specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() jobRepo.On("GetByJobName", ctx, sampleTenant.ProjectName(), specA.Name()).Return(nil, optErrors.NotFound(job.EntityJob, "job not found")) - jobService := service.NewJobService(jobRepo, nil, nil, nil, nil) + jobService := service.NewJobService(jobRepo, nil, nil, nil, nil, nil, nil, log, nil, nil) actual, err := jobService.GetByFilter(ctx, filter.WithString(filter.ProjectName, sampleTenant.ProjectName().String()), filter.WithString(filter.JobName, specA.Name().String()), @@ -1636,7 +2660,7 @@ func TestJobService(t *testing.T) { jobA := job.NewJob(sampleTenant, specA, "table-A", []job.ResourceURN{"table-B"}) jobRepo.On("GetByJobName", ctx, sampleTenant.ProjectName(), specA.Name()).Return(jobA, nil) - jobService := service.NewJobService(jobRepo, nil, nil, nil, nil) + jobService := service.NewJobService(jobRepo, nil, nil, nil, nil, nil, nil, log, nil, nil) actual, err := jobService.GetByFilter(ctx, filter.WithString(filter.ProjectName, sampleTenant.ProjectName().String()), filter.WithString(filter.JobName, specA.Name().String()), @@ -1654,7 +2678,7 @@ func TestJobService(t *testing.T) { jobRepo.On("GetAllByTenant", ctx, sampleTenant).Return(nil, errors.New("error encountered")) - jobService := service.NewJobService(jobRepo, nil, nil, nil, nil) + jobService := service.NewJobService(jobRepo, nil, nil, nil, nil, nil, nil, log, nil, nil) actual, err := jobService.GetByFilter(ctx, filter.WithString(filter.ProjectName, sampleTenant.ProjectName().String()), filter.WithStringArray(filter.NamespaceNames, []string{sampleTenant.NamespaceName().String()}), @@ -1666,7 +2690,7 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) - jobService := service.NewJobService(jobRepo, nil, nil, nil, nil) + jobService := service.NewJobService(jobRepo, nil, nil, nil, nil, nil, nil, log, nil, nil) actual, err := jobService.GetByFilter(ctx, filter.WithString(filter.ProjectName, sampleTenant.ProjectName().String()), filter.WithStringArray(filter.NamespaceNames, []string{""}), @@ -1682,7 +2706,7 @@ func TestJobService(t *testing.T) { jobA := job.NewJob(sampleTenant, specA, "table-A", []job.ResourceURN{"table-B"}) jobRepo.On("GetAllByTenant", ctx, sampleTenant).Return([]*job.Job{jobA}, nil) - jobService := service.NewJobService(jobRepo, nil, nil, nil, nil) + jobService := service.NewJobService(jobRepo, nil, nil, nil, nil, nil, nil, log, nil, nil) actual, err := jobService.GetByFilter(ctx, filter.WithString(filter.ProjectName, sampleTenant.ProjectName().String()), filter.WithStringArray(filter.NamespaceNames, []string{sampleTenant.NamespaceName().String()}), @@ -1700,7 +2724,7 @@ func TestJobService(t *testing.T) { jobRepo.On("GetAllByTenant", ctx, sampleTenant).Return(nil, errors.New("error encountered")) - jobService := service.NewJobService(jobRepo, nil, nil, nil, nil) + jobService := service.NewJobService(jobRepo, nil, nil, nil, nil, nil, nil, log, nil, nil) actual, err := jobService.GetByFilter(ctx, filter.WithString(filter.ProjectName, sampleTenant.ProjectName().String()), filter.WithString(filter.NamespaceName, sampleTenant.NamespaceName().String()), @@ -1716,7 +2740,7 @@ func TestJobService(t *testing.T) { jobA := job.NewJob(sampleTenant, specA, "table-A", []job.ResourceURN{"table-B"}) jobRepo.On("GetAllByTenant", ctx, sampleTenant).Return([]*job.Job{jobA}, nil) - jobService := service.NewJobService(jobRepo, nil, nil, nil, nil) + jobService := service.NewJobService(jobRepo, nil, nil, nil, nil, nil, nil, log, nil, nil) actual, err := jobService.GetByFilter(ctx, filter.WithString(filter.ProjectName, sampleTenant.ProjectName().String()), filter.WithString(filter.NamespaceName, sampleTenant.NamespaceName().String()), @@ -1734,7 +2758,7 @@ func TestJobService(t *testing.T) { jobRepo.On("GetAllByProjectName", ctx, sampleTenant.ProjectName()).Return(nil, errors.New("error encountered")) - jobService := service.NewJobService(jobRepo, nil, nil, nil, nil) + jobService := service.NewJobService(jobRepo, nil, nil, nil, nil, nil, nil, log, nil, nil) actual, err := jobService.GetByFilter(ctx, filter.WithString(filter.ProjectName, sampleTenant.ProjectName().String()), ) @@ -1749,7 +2773,7 @@ func TestJobService(t *testing.T) { jobA := job.NewJob(sampleTenant, specA, "table-A", []job.ResourceURN{"table-B"}) jobRepo.On("GetAllByProjectName", ctx, sampleTenant.ProjectName()).Return([]*job.Job{jobA}, nil) - jobService := service.NewJobService(jobRepo, nil, nil, nil, nil) + jobService := service.NewJobService(jobRepo, nil, nil, nil, nil, nil, nil, log, nil, nil) actual, err := jobService.GetByFilter(ctx, filter.WithString(filter.ProjectName, sampleTenant.ProjectName().String()), ) @@ -1760,7 +2784,7 @@ func TestJobService(t *testing.T) { }) }) t.Run("return error when there's no filter", func(t *testing.T) { - jobService := service.NewJobService(nil, nil, nil, nil, nil) + jobService := service.NewJobService(nil, nil, nil, nil, nil, nil, nil, log, nil, nil) actual, err := jobService.GetByFilter(ctx) assert.Error(t, err, "no filter matched") assert.Nil(t, actual) @@ -1774,7 +2798,8 @@ func TestJobService(t *testing.T) { pluginService.On("Info", ctx, jobTask.Name()).Return(nil, errors.New("error encountered")) - jobService := service.NewJobService(nil, pluginService, nil, nil, nil) + jobService := service.NewJobService(nil, nil, nil, pluginService, nil, nil, nil, nil, nil, nil) + actual, err := jobService.GetTaskInfo(ctx, jobTask) assert.Error(t, err, "error encountered") assert.Nil(t, actual) @@ -1786,11 +2811,12 @@ func TestJobService(t *testing.T) { pluginInfoResp := &plugin.Info{ Name: "bq2bq", Description: "plugin desc", - Image: "odpf/bq2bq:latest", + Image: "raystack/bq2bq:latest", } pluginService.On("Info", ctx, jobTask.Name()).Return(pluginInfoResp, nil) - jobService := service.NewJobService(nil, pluginService, nil, nil, nil) + jobService := service.NewJobService(nil, nil, nil, pluginService, nil, nil, nil, nil, nil, nil) + actual, err := jobService.GetTaskInfo(ctx, jobTask) assert.NoError(t, err) assert.NotNil(t, actual) @@ -1804,10 +2830,46 @@ func TestJobService(t *testing.T) { tenantDetailsGetter.On("GetDetails", ctx, mock.Anything).Return(nil, errors.New("get tenant details fail")) defer tenantDetailsGetter.AssertExpectations(t) - jobService := service.NewJobService(nil, nil, nil, tenantDetailsGetter, log) - err := jobService.Validate(ctx, sampleTenant, []*job.Spec{}, nil) + jobService := service.NewJobService(nil, nil, nil, nil, nil, tenantDetailsGetter, nil, log, nil, nil) + err := jobService.Validate(ctx, sampleTenant, []*job.Spec{}, jobNamesWithInvalidSpec, nil) + assert.Error(t, err) + assert.Equal(t, "get tenant details fail", err.Error()) + }) + t.Run("returns error when validate duplicate jobs detected", func(t *testing.T) { + tenantDetailsGetter := new(TenantDetailsGetter) + tenantDetailsGetter.On("GetDetails", ctx, mock.Anything).Return(detailedTenant, nil) + defer tenantDetailsGetter.AssertExpectations(t) + + pluginService := new(PluginService) + defer pluginService.AssertExpectations(t) + + upstreamResolver := new(UpstreamResolver) + defer upstreamResolver.AssertExpectations(t) + + jobRepo := new(JobRepository) + defer jobRepo.AssertExpectations(t) + + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + + logWriter := new(mockWriter) + defer logWriter.AssertExpectations(t) + + specA1, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + specA2, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + + jobRepo.On("GetAllByTenant", ctx, sampleTenant).Return(nil, nil) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) + + logWriter.On("Write", mock.Anything, mock.Anything).Return(nil) + + err := jobService.Validate(ctx, sampleTenant, []*job.Spec{specA1, specA2}, jobNamesWithInvalidSpec, logWriter) assert.Error(t, err) - assert.Equal(t, "get tenant details fail", err.Error()) + assert.Equal(t, "validate specs errors:\n duplicate job-A", err.Error()) }) t.Run("returns error when generate jobs", func(t *testing.T) { tenantDetailsGetter := new(TenantDetailsGetter) @@ -1820,22 +2882,33 @@ func TestJobService(t *testing.T) { upstreamResolver := new(UpstreamResolver) defer upstreamResolver.AssertExpectations(t) + jobRepo := new(JobRepository) + defer jobRepo.AssertExpectations(t) + + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + logWriter := new(mockWriter) defer logWriter.AssertExpectations(t) specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() - specs := []*job.Spec{specA} + + jobRepo.On("GetAllByTenant", ctx, sampleTenant).Return(nil, nil) pluginService.On("GenerateDestination", ctx, detailedTenant, specA.Task()).Return(job.ResourceURN(""), errors.New("some error on generate destination")) - jobService := service.NewJobService(nil, pluginService, upstreamResolver, tenantDetailsGetter, log) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) logWriter.On("Write", mock.Anything, mock.Anything).Return(nil) - err := jobService.Validate(ctx, sampleTenant, specs, logWriter) + err := jobService.Validate(ctx, sampleTenant, []*job.Spec{specA}, jobNamesWithInvalidSpec, logWriter) assert.Error(t, err) assert.Equal(t, "validate specs errors:\n internal error for entity job: unable to add job-A: some error on generate destination", err.Error()) }) - t.Run("returns error when get all by project name failed", func(t *testing.T) { + t.Run("returns error when a job fail a deletion check", func(t *testing.T) { tenantDetailsGetter := new(TenantDetailsGetter) tenantDetailsGetter.On("GetDetails", ctx, mock.Anything).Return(detailedTenant, nil) defer tenantDetailsGetter.AssertExpectations(t) @@ -1846,26 +2919,120 @@ func TestJobService(t *testing.T) { upstreamResolver := new(UpstreamResolver) defer upstreamResolver.AssertExpectations(t) + jobRepo := new(JobRepository) + defer jobRepo.AssertExpectations(t) + + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + logWriter := new(mockWriter) defer logWriter.AssertExpectations(t) - repo := new(JobRepository) - repo.On("GetAllByProjectName", ctx, sampleTenant.ProjectName()).Return(nil, errors.New("error on get all by project name")) - defer repo.AssertExpectations(t) + jobTaskConfigA, _ := job.ConfigFrom(map[string]string{"table": "table-A"}) + jobTaskConfigB, _ := job.ConfigFrom(map[string]string{"table": "table-B"}) + jobTaskConfigC, _ := job.ConfigFrom(map[string]string{"table": "table-C"}) + jobTaskConfigD, _ := job.ConfigFrom(map[string]string{"table": "table-D"}) + jobTaskA := job.NewTask(taskName, jobTaskConfigA) + jobTaskB := job.NewTask(taskName, jobTaskConfigB) + jobTaskC := job.NewTask(taskName, jobTaskConfigC) + jobTaskD := job.NewTask(taskName, jobTaskConfigD) + + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTaskA).Build() + specAUpdated, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner-updated", jobSchedule, jobWindow, jobTaskA).Build() + specB, _ := job.NewSpecBuilder(jobVersion, "job-B", "sample-owner", jobSchedule, jobWindow, jobTaskB).Build() + specC, _ := job.NewSpecBuilder(jobVersion, "job-C", "sample-owner", jobSchedule, jobWindow, jobTaskC).Build() + specD, _ := job.NewSpecBuilder(jobVersion, "job-D", "sample-owner", jobSchedule, jobWindow, jobTaskD).Build() + specs := []*job.Spec{specAUpdated, specB, specD} + + jobA := job.NewJob(sampleTenant, specA, "table-A", []job.ResourceURN{"table-Z"}) + jobB := job.NewJob(sampleTenant, specB, "table-B", []job.ResourceURN{"table-A", "table-D"}) + jobC := job.NewJob(sampleTenant, specC, "table-C", []job.ResourceURN{"table-B"}) + jobD := job.NewJob(sampleTenant, specD, "table-D", nil) + + jobRepo.On("GetAllByTenant", ctx, sampleTenant).Return([]*job.Job{jobA, jobB, jobC, jobD}, nil) - specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() - specs := []*job.Spec{specA} + pluginService.On("GenerateDestination", ctx, detailedTenant, jobTaskA).Return(job.ResourceURN("table-A"), nil) + pluginService.On("GenerateUpstreams", ctx, detailedTenant, specAUpdated, true).Return([]job.ResourceURN{"table-Z"}, nil) - pluginService.On("GenerateDestination", ctx, detailedTenant, specA.Task()).Return(job.ResourceURN("example_destination"), nil) - pluginService.On("GenerateUpstreams", ctx, detailedTenant, specA, true).Return([]job.ResourceURN{"example_upstream", "another_upstream"}, nil) + jobCDownstream := []*job.Downstream{ + job.NewDownstream("job-B", project.Name(), namespace.Name(), taskName), + } + jobBDownstream := []*job.Downstream{ + job.NewDownstream("job-D", project.Name(), namespace.Name(), taskName), + } + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), specC.Name()).Return(jobCDownstream, nil) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), specB.Name()).Return(jobBDownstream, nil) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), specD.Name()).Return([]*job.Downstream{}, nil) logWriter.On("Write", mock.Anything, mock.Anything).Return(nil) - jobService := service.NewJobService(repo, pluginService, upstreamResolver, tenantDetailsGetter, log) - err := jobService.Validate(ctx, sampleTenant, specs, logWriter) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) + err := jobService.Validate(ctx, sampleTenant, specs, jobNamesWithInvalidSpec, logWriter) assert.Error(t, err) - assert.Equal(t, "error on get all by project name", err.Error()) + assert.ErrorContains(t, err, "deletion of job job-C will fail. job is being used by test-proj/job-B, test-proj/job-D") + }) + + t.Run("returns no error when delete the job and its downstreams", func(t *testing.T) { + tenantDetailsGetter := new(TenantDetailsGetter) + tenantDetailsGetter.On("GetDetails", ctx, mock.Anything).Return(detailedTenant, nil) + defer tenantDetailsGetter.AssertExpectations(t) + + pluginService := new(PluginService) + defer pluginService.AssertExpectations(t) + + upstreamResolver := new(UpstreamResolver) + defer upstreamResolver.AssertExpectations(t) + + jobRepo := new(JobRepository) + defer jobRepo.AssertExpectations(t) + + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + + logWriter := new(mockWriter) + defer logWriter.AssertExpectations(t) + + jobTaskConfigA, _ := job.ConfigFrom(map[string]string{"table": "table-A"}) + jobTaskConfigB, _ := job.ConfigFrom(map[string]string{"table": "table-B"}) + jobTaskConfigC, _ := job.ConfigFrom(map[string]string{"table": "table-C"}) + jobTaskA := job.NewTask(taskName, jobTaskConfigA) + jobTaskB := job.NewTask(taskName, jobTaskConfigB) + jobTaskC := job.NewTask(taskName, jobTaskConfigC) + + specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTaskA).Build() + specAUpdated, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner-updated", jobSchedule, jobWindow, jobTaskA).Build() + specB, _ := job.NewSpecBuilder(jobVersion, "job-B", "sample-owner", jobSchedule, jobWindow, jobTaskB).Build() + specC, _ := job.NewSpecBuilder(jobVersion, "job-C", "sample-owner", jobSchedule, jobWindow, jobTaskC).Build() + specs := []*job.Spec{specAUpdated} + + jobA := job.NewJob(sampleTenant, specA, "table-A", []job.ResourceURN{"table-Z"}) + jobB := job.NewJob(sampleTenant, specB, "table-B", nil) + jobC := job.NewJob(sampleTenant, specC, "table-C", []job.ResourceURN{"table-B"}) + + jobRepo.On("GetAllByTenant", ctx, sampleTenant).Return([]*job.Job{jobA, jobB, jobC}, nil) + + pluginService.On("GenerateDestination", ctx, detailedTenant, jobTaskA).Return(job.ResourceURN("table-A"), nil) + pluginService.On("GenerateUpstreams", ctx, detailedTenant, specAUpdated, true).Return([]job.ResourceURN{"table-Z"}, nil) + + jobCDownstream := []*job.Downstream{ + job.NewDownstream("job-B", project.Name(), namespace.Name(), taskName), + } + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), specC.Name()).Return(jobCDownstream, nil) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), specB.Name()).Return([]*job.Downstream{}, nil) + + logWriter.On("Write", mock.Anything, mock.Anything).Return(nil) + + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) + err := jobService.Validate(ctx, sampleTenant, specs, jobNamesWithInvalidSpec, logWriter) + assert.NoError(t, err) }) + t.Run("returns error when there's a cyclic", func(t *testing.T) { tenantDetailsGetter := new(TenantDetailsGetter) tenantDetailsGetter.On("GetDetails", ctx, mock.Anything).Return(detailedTenant, nil) @@ -1877,8 +3044,14 @@ func TestJobService(t *testing.T) { upstreamResolver := new(UpstreamResolver) defer upstreamResolver.AssertExpectations(t) - repo := new(JobRepository) - defer repo.AssertExpectations(t) + jobRepo := new(JobRepository) + defer jobRepo.AssertExpectations(t) + + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) logWriter := new(mockWriter) defer logWriter.AssertExpectations(t) @@ -1886,21 +3059,19 @@ func TestJobService(t *testing.T) { specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() specB, _ := job.NewSpecBuilder(jobVersion, "job-B", "sample-owner", jobSchedule, jobWindow, jobTask).Build() specC, _ := job.NewSpecBuilder(jobVersion, "job-C", "sample-owner", jobSchedule, jobWindow, jobTask).Build() - specs := []*job.Spec{specA} - jobA := job.NewJob(sampleTenant, specA, "table-A", []job.ResourceURN{"table-C"}) jobB := job.NewJob(sampleTenant, specB, "table-B", []job.ResourceURN{"table-A"}) jobC := job.NewJob(sampleTenant, specC, "table-C", []job.ResourceURN{"table-B"}) - repo.On("GetAllByProjectName", ctx, sampleTenant.ProjectName()).Return([]*job.Job{jobA, jobB, jobC}, nil) + jobRepo.On("GetAllByTenant", ctx, sampleTenant).Return([]*job.Job{jobB, jobC}, nil) pluginService.On("GenerateDestination", ctx, detailedTenant, jobTask).Return(job.ResourceURN("table-A"), nil) pluginService.On("GenerateUpstreams", ctx, detailedTenant, specA, true).Return([]job.ResourceURN{"table-C"}, nil) logWriter.On("Write", mock.Anything, mock.Anything).Return(nil) - jobService := service.NewJobService(repo, pluginService, upstreamResolver, tenantDetailsGetter, log) - err := jobService.Validate(ctx, sampleTenant, specs, logWriter) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) + err := jobService.Validate(ctx, sampleTenant, []*job.Spec{specA, specB, specC}, jobNamesWithInvalidSpec, logWriter) assert.Error(t, err) assert.ErrorContains(t, err, "a cycle dependency encountered in the tree:") }) @@ -1915,8 +3086,14 @@ func TestJobService(t *testing.T) { upstreamResolver := new(UpstreamResolver) defer upstreamResolver.AssertExpectations(t) - repo := new(JobRepository) - defer repo.AssertExpectations(t) + jobRepo := new(JobRepository) + defer jobRepo.AssertExpectations(t) + + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) logWriter := new(mockWriter) defer logWriter.AssertExpectations(t) @@ -1929,27 +3106,23 @@ func TestJobService(t *testing.T) { jobTaskC := job.NewTask(taskName, jobTaskConfigC) specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTaskA).Build() + specAUpdated, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner-updated", jobSchedule, jobWindow, jobTaskA).Build() specB, _ := job.NewSpecBuilder(jobVersion, "job-B", "sample-owner", jobSchedule, jobWindow, jobTaskB).Build() specC, _ := job.NewSpecBuilder(jobVersion, "job-C", "sample-owner", jobSchedule, jobWindow, jobTaskC).Build() - specs := []*job.Spec{specA, specB, specC} jobA := job.NewJob(sampleTenant, specA, "table-A", []job.ResourceURN{"table-Z"}) jobB := job.NewJob(sampleTenant, specB, "table-B", []job.ResourceURN{"table-A"}) jobC := job.NewJob(sampleTenant, specC, "table-C", []job.ResourceURN{"table-B"}) - repo.On("GetAllByProjectName", ctx, sampleTenant.ProjectName()).Return([]*job.Job{jobA, jobB, jobC}, nil) + jobRepo.On("GetAllByTenant", ctx, sampleTenant).Return([]*job.Job{jobA, jobB, jobC}, nil) pluginService.On("GenerateDestination", ctx, detailedTenant, jobTaskA).Return(job.ResourceURN("table-A"), nil) - pluginService.On("GenerateDestination", ctx, detailedTenant, jobTaskB).Return(job.ResourceURN("table-B"), nil) - pluginService.On("GenerateDestination", ctx, detailedTenant, jobTaskC).Return(job.ResourceURN("table-C"), nil) - pluginService.On("GenerateUpstreams", ctx, detailedTenant, specA, true).Return([]job.ResourceURN{"table-C", "table-Z"}, nil) - pluginService.On("GenerateUpstreams", ctx, detailedTenant, specB, true).Return([]job.ResourceURN{"table-A"}, nil) - pluginService.On("GenerateUpstreams", ctx, detailedTenant, specC, true).Return([]job.ResourceURN{"table-B"}, nil) + pluginService.On("GenerateUpstreams", ctx, detailedTenant, specAUpdated, true).Return([]job.ResourceURN{"table-C", "table-Z"}, nil) logWriter.On("Write", mock.Anything, mock.Anything).Return(nil) - jobService := service.NewJobService(repo, pluginService, upstreamResolver, tenantDetailsGetter, log) - err := jobService.Validate(ctx, sampleTenant, specs, logWriter) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) + err := jobService.Validate(ctx, sampleTenant, []*job.Spec{specAUpdated, specB, specC}, jobNamesWithInvalidSpec, logWriter) assert.Error(t, err) assert.ErrorContains(t, err, "a cycle dependency encountered in the tree:") }) @@ -1964,8 +3137,14 @@ func TestJobService(t *testing.T) { upstreamResolver := new(UpstreamResolver) defer upstreamResolver.AssertExpectations(t) - repo := new(JobRepository) - defer repo.AssertExpectations(t) + jobRepo := new(JobRepository) + defer jobRepo.AssertExpectations(t) + + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) logWriter := new(mockWriter) defer logWriter.AssertExpectations(t) @@ -1980,26 +3159,27 @@ func TestJobService(t *testing.T) { upstreamsSpecA, _ := job.NewSpecUpstreamBuilder().WithUpstreamNames([]job.SpecUpstreamName{"test-proj/job-C"}).Build() upstreamsSpecC, _ := job.NewSpecUpstreamBuilder().WithUpstreamNames([]job.SpecUpstreamName{"test-proj/job-B"}).Build() specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTaskA).WithSpecUpstream(upstreamsSpecA).Build() + specAUpdated, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner-updated", jobSchedule, jobWindow, jobTaskA).WithSpecUpstream(upstreamsSpecA).Build() specB, _ := job.NewSpecBuilder(jobVersion, "job-B", "sample-owner", jobSchedule, jobWindow, jobTaskB).Build() + specBUpdated, _ := job.NewSpecBuilder(jobVersion, "job-B", "sample-owner-updated", jobSchedule, jobWindow, jobTaskB).Build() specC, _ := job.NewSpecBuilder(jobVersion, "job-C", "sample-owner", jobSchedule, jobWindow, jobTaskC).WithSpecUpstream(upstreamsSpecC).Build() - specs := []*job.Spec{specA, specB, specC} jobA := job.NewJob(sampleTenant, specA, "table-A", []job.ResourceURN{"table-Z"}) jobB := job.NewJob(sampleTenant, specB, "table-B", []job.ResourceURN{"table-A"}) - repo.On("GetAllByProjectName", ctx, sampleTenant.ProjectName()).Return([]*job.Job{jobA, jobB}, nil) + jobRepo.On("GetAllByTenant", ctx, sampleTenant).Return([]*job.Job{jobA, jobB}, nil) pluginService.On("GenerateDestination", ctx, detailedTenant, jobTaskA).Return(job.ResourceURN("table-A"), nil) pluginService.On("GenerateDestination", ctx, detailedTenant, jobTaskB).Return(job.ResourceURN("table-B"), nil) pluginService.On("GenerateDestination", ctx, detailedTenant, jobTaskC).Return(job.ResourceURN(""), service.ErrUpstreamModNotFound) - pluginService.On("GenerateUpstreams", ctx, detailedTenant, specA, true).Return([]job.ResourceURN{"table-Z"}, nil) - pluginService.On("GenerateUpstreams", ctx, detailedTenant, specB, true).Return([]job.ResourceURN{"table-A"}, nil) + pluginService.On("GenerateUpstreams", ctx, detailedTenant, specAUpdated, true).Return([]job.ResourceURN{"table-Z"}, nil) + pluginService.On("GenerateUpstreams", ctx, detailedTenant, specBUpdated, true).Return([]job.ResourceURN{"table-A"}, nil) pluginService.On("GenerateUpstreams", ctx, detailedTenant, specC, true).Return(nil, service.ErrUpstreamModNotFound) logWriter.On("Write", mock.Anything, mock.Anything).Return(nil) - jobService := service.NewJobService(repo, pluginService, upstreamResolver, tenantDetailsGetter, log) - err := jobService.Validate(ctx, sampleTenant, specs, logWriter) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) + err := jobService.Validate(ctx, sampleTenant, []*job.Spec{specAUpdated, specBUpdated, specC}, jobNamesWithInvalidSpec, logWriter) assert.Error(t, err) assert.ErrorContains(t, err, "a cycle dependency encountered in the tree:") }) @@ -2014,8 +3194,14 @@ func TestJobService(t *testing.T) { upstreamResolver := new(UpstreamResolver) defer upstreamResolver.AssertExpectations(t) - repo := new(JobRepository) - defer repo.AssertExpectations(t) + jobRepo := new(JobRepository) + defer jobRepo.AssertExpectations(t) + + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) logWriter := new(mockWriter) defer logWriter.AssertExpectations(t) @@ -2029,15 +3215,15 @@ func TestJobService(t *testing.T) { specC, _ := job.NewSpecBuilder(jobVersion, "job-C", "sample-owner", jobSchedule, jobWindow, jobTaskPython).WithSpecUpstream(upstreamsSpecC).Build() specs := []*job.Spec{specA, specB, specC} - repo.On("GetAllByProjectName", ctx, sampleTenant.ProjectName()).Return([]*job.Job{}, nil) + jobRepo.On("GetAllByTenant", ctx, sampleTenant).Return([]*job.Job{}, nil) pluginService.On("GenerateDestination", ctx, detailedTenant, mock.Anything).Return(job.ResourceURN(""), service.ErrUpstreamModNotFound) pluginService.On("GenerateUpstreams", ctx, detailedTenant, mock.Anything, true).Return(nil, service.ErrUpstreamModNotFound) logWriter.On("Write", mock.Anything, mock.Anything).Return(nil) - jobService := service.NewJobService(repo, pluginService, upstreamResolver, tenantDetailsGetter, log) - err := jobService.Validate(ctx, sampleTenant, specs, logWriter) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) + err := jobService.Validate(ctx, sampleTenant, specs, jobNamesWithInvalidSpec, logWriter) assert.Error(t, err) assert.ErrorContains(t, err, "a cycle dependency encountered in the tree:") }) @@ -2052,8 +3238,14 @@ func TestJobService(t *testing.T) { upstreamResolver := new(UpstreamResolver) defer upstreamResolver.AssertExpectations(t) - repo := new(JobRepository) - defer repo.AssertExpectations(t) + jobRepo := new(JobRepository) + defer jobRepo.AssertExpectations(t) + + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) logWriter := new(mockWriter) defer logWriter.AssertExpectations(t) @@ -2066,27 +3258,25 @@ func TestJobService(t *testing.T) { jobTaskC := job.NewTask(taskName, jobTaskConfigC) specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTaskA).Build() + specAUpdated, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner-updated", jobSchedule, jobWindow, jobTaskA).Build() specB, _ := job.NewSpecBuilder(jobVersion, "job-B", "sample-owner", jobSchedule, jobWindow, jobTaskB).Build() specC, _ := job.NewSpecBuilder(jobVersion, "job-C", "sample-owner", jobSchedule, jobWindow, jobTaskC).Build() - specs := []*job.Spec{specA, specB, specC} + specs := []*job.Spec{specAUpdated, specB} jobA := job.NewJob(sampleTenant, specA, "table-A", []job.ResourceURN{"table-Z"}) jobB := job.NewJob(sampleTenant, specB, "table-B", []job.ResourceURN{"table-A"}) jobC := job.NewJob(sampleTenant, specC, "table-C", []job.ResourceURN{"table-B"}) - repo.On("GetAllByProjectName", ctx, sampleTenant.ProjectName()).Return([]*job.Job{jobA, jobB, jobC}, nil) + jobRepo.On("GetAllByTenant", ctx, sampleTenant).Return([]*job.Job{jobA, jobB, jobC}, nil) pluginService.On("GenerateDestination", ctx, detailedTenant, jobTaskA).Return(job.ResourceURN("table-A"), nil) - pluginService.On("GenerateDestination", ctx, detailedTenant, jobTaskB).Return(job.ResourceURN("table-B"), nil) - pluginService.On("GenerateDestination", ctx, detailedTenant, jobTaskC).Return(job.ResourceURN("table-C"), nil) - pluginService.On("GenerateUpstreams", ctx, detailedTenant, specA, true).Return([]job.ResourceURN{"table-Z"}, nil) - pluginService.On("GenerateUpstreams", ctx, detailedTenant, specB, true).Return([]job.ResourceURN{"table-A"}, nil) - pluginService.On("GenerateUpstreams", ctx, detailedTenant, specC, true).Return([]job.ResourceURN{"table-B"}, nil) + pluginService.On("GenerateUpstreams", ctx, detailedTenant, specAUpdated, true).Return([]job.ResourceURN{"table-Z"}, nil) - logWriter.On("Write", mock.Anything, mock.Anything).Return(nil) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), specC.Name()).Return([]*job.Downstream{}, nil) - jobService := service.NewJobService(repo, pluginService, upstreamResolver, tenantDetailsGetter, log) - err := jobService.Validate(ctx, sampleTenant, specs, logWriter) + logWriter.On("Write", mock.Anything, mock.Anything).Return(nil) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) + err := jobService.Validate(ctx, sampleTenant, specs, jobNamesWithInvalidSpec, logWriter) assert.NoError(t, err) }) }) @@ -2096,6 +3286,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -2110,9 +3306,9 @@ func TestJobService(t *testing.T) { upstreamB := job.NewUpstreamResolved("job-B", "", "resource-B", sampleTenant, "inferred", taskName, false) - jobRepo.On("GetUpstreams", ctx, project.Name(), jobA.Spec().Name()).Return([]*job.Upstream{upstreamB}, nil) + upstreamRepo.On("GetUpstreams", ctx, project.Name(), jobA.Spec().Name()).Return([]*job.Upstream{upstreamB}, nil) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) result, err := jobService.GetUpstreamsToInspect(ctx, jobA, false) assert.NoError(t, err) assert.EqualValues(t, []*job.Upstream{upstreamB}, result) @@ -2121,6 +3317,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -2140,7 +3342,7 @@ func TestJobService(t *testing.T) { upstreamResolver.On("Resolve", ctx, jobA, mock.Anything).Return([]*job.Upstream{upstreamB}, nil) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) result, err := jobService.GetUpstreamsToInspect(ctx, jobA, true) assert.NoError(t, err) assert.EqualValues(t, []*job.Upstream{upstreamB}, result) @@ -2152,6 +3354,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -2175,7 +3383,7 @@ func TestJobService(t *testing.T) { jobA := job.NewJob(sampleTenant, specA, jobADestination, jobASources) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) result, logger := jobService.GetJobBasicInfo(ctx, sampleTenant, "", specA) assert.Nil(t, logger.Messages) assert.Equal(t, jobA, result) @@ -2184,6 +3392,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -2201,7 +3415,7 @@ func TestJobService(t *testing.T) { jobRepo.On("GetByJobName", ctx, project.Name(), specA.Name()).Return(jobA, nil) jobRepo.On("GetAllByResourceDestination", ctx, jobADestination).Return([]*job.Job{}, nil) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) result, logger := jobService.GetJobBasicInfo(ctx, sampleTenant, specA.Name(), nil) assert.Nil(t, logger.Messages) assert.Equal(t, jobA, result) @@ -2210,6 +3424,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -2223,7 +3443,7 @@ func TestJobService(t *testing.T) { tenantDetailsGetter.On("GetDetails", ctx, sampleTenant).Return(detailedTenant, errors.New("sample error")) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) result, logger := jobService.GetJobBasicInfo(ctx, sampleTenant, "", specA) assert.Contains(t, logger.Messages[0].Message, "sample error") assert.Nil(t, result) @@ -2232,6 +3452,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -2251,7 +3477,7 @@ func TestJobService(t *testing.T) { jobAUpstreamName := []job.ResourceURN{"job-B"} pluginService.On("GenerateUpstreams", ctx, detailedTenant, specA, true).Return(jobAUpstreamName, errors.New("sample error")) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) result, logger := jobService.GetJobBasicInfo(ctx, sampleTenant, "", specA) assert.Contains(t, logger.Messages[0].Message, "sample error") assert.Nil(t, result) @@ -2260,6 +3486,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -2269,7 +3501,7 @@ func TestJobService(t *testing.T) { tenantDetailsGetter := new(TenantDetailsGetter) defer tenantDetailsGetter.AssertExpectations(t) - specASchedule, err := job.NewScheduleBuilder(startDate).WithCatchUp(true).Build() + specASchedule, err := job.NewScheduleBuilder(startDate).Build() assert.NoError(t, err) specA, _ := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", specASchedule, jobWindow, jobTask).Build() @@ -2279,17 +3511,22 @@ func TestJobService(t *testing.T) { jobRepo.On("GetByJobName", ctx, project.Name(), specA.Name()).Return(jobA, nil) jobRepo.On("GetAllByResourceDestination", ctx, jobADestination).Return([]*job.Job{}, errors.New("sample-error")) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) result, logger := jobService.GetJobBasicInfo(ctx, sampleTenant, specA.Name(), nil) assert.Contains(t, logger.Messages[0].Message, "no job sources detected") - assert.Contains(t, logger.Messages[1].Message, "catchup is enabled") - assert.Contains(t, logger.Messages[2].Message, "could not perform duplicate job destination check") + assert.Contains(t, logger.Messages[1].Message, "could not perform duplicate job destination check") assert.Equal(t, jobA, result) }) t.Run("should return error if unable to get existing job", func(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -2303,7 +3540,7 @@ func TestJobService(t *testing.T) { jobRepo.On("GetByJobName", ctx, project.Name(), specA.Name()).Return(nil, errors.New("internal error")) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) result, logger := jobService.GetJobBasicInfo(ctx, sampleTenant, specA.Name(), nil) assert.Contains(t, logger.Messages[0].Message, "internal error") assert.Nil(t, result) @@ -2312,6 +3549,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -2325,7 +3568,7 @@ func TestJobService(t *testing.T) { jobRepo.On("GetByJobName", ctx, project.Name(), specA.Name()).Return(nil, errors.New("job not found")) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) result, logger := jobService.GetJobBasicInfo(ctx, sampleTenant, specA.Name(), nil) assert.Contains(t, logger.Messages[0].Message, "job not found") assert.Nil(t, result) @@ -2334,6 +3577,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -2356,7 +3605,7 @@ func TestJobService(t *testing.T) { jobRepo.On("GetByJobName", ctx, project.Name(), specA.Name()).Return(jobA, nil) jobRepo.On("GetAllByResourceDestination", ctx, jobADestination).Return([]*job.Job{jobB, jobA}, nil) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) result, logger := jobService.GetJobBasicInfo(ctx, sampleTenant, specA.Name(), nil) assert.Contains(t, logger.Messages[0].Message, "job already exists with same Destination") assert.Equal(t, jobA, result) @@ -2365,6 +3614,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -2382,7 +3637,7 @@ func TestJobService(t *testing.T) { jobRepo.On("GetByJobName", ctx, project.Name(), specA.Name()).Return(jobA, nil) jobRepo.On("GetAllByResourceDestination", ctx, jobADestination).Return([]*job.Job{jobA}, nil) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) result, logger := jobService.GetJobBasicInfo(ctx, sampleTenant, specA.Name(), nil) assert.Nil(t, logger.Messages) assert.Equal(t, jobA, result) @@ -2394,6 +3649,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -2409,9 +3670,9 @@ func TestJobService(t *testing.T) { jobADownstream := []*job.Downstream{ job.NewDownstream("job-B", project.Name(), namespace.Name(), taskName), } - jobRepo.On("GetDownstreamByDestination", ctx, project.Name(), jobA.Destination()).Return(jobADownstream, nil) + downstreamRepo.On("GetDownstreamByDestination", ctx, project.Name(), jobA.Destination()).Return(jobADownstream, nil) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) result, err := jobService.GetDownstream(ctx, jobA, true) assert.NoError(t, err) assert.Equal(t, jobADownstream, result) @@ -2420,6 +3681,12 @@ func TestJobService(t *testing.T) { jobRepo := new(JobRepository) defer jobRepo.AssertExpectations(t) + upstreamRepo := new(UpstreamRepository) + defer upstreamRepo.AssertExpectations(t) + + downstreamRepo := new(DownstreamRepository) + defer downstreamRepo.AssertExpectations(t) + pluginService := new(PluginService) defer pluginService.AssertExpectations(t) @@ -2435,14 +3702,60 @@ func TestJobService(t *testing.T) { jobADownstream := []*job.Downstream{ job.NewDownstream("job-B", project.Name(), namespace.Name(), taskName), } - jobRepo.On("GetDownstreamByJobName", ctx, project.Name(), specA.Name()).Return(jobADownstream, nil) + downstreamRepo.On("GetDownstreamByJobName", ctx, project.Name(), specA.Name()).Return(jobADownstream, nil) - jobService := service.NewJobService(jobRepo, pluginService, upstreamResolver, tenantDetailsGetter, log) + jobService := service.NewJobService(jobRepo, upstreamRepo, downstreamRepo, pluginService, upstreamResolver, tenantDetailsGetter, nil, log, nil, nil) result, err := jobService.GetDownstream(ctx, jobA, false) assert.NoError(t, err) assert.Equal(t, jobADownstream, result) }) }) + t.Run("updateState", func(t *testing.T) { + jobName, _ := job.NameFrom("job-A") + jobsToUpdateState := []job.Name{jobName} + state := job.DISABLED + updateRemark := "job disable remark" + t.Run("should fail if scheduler state change request fails", func(t *testing.T) { + scheduler := new(mockScheduler) + scheduler.On("UpdateJobState", ctx, sampleTenant, jobsToUpdateState, state.String()).Return(fmt.Errorf("some error in update Job State")) + defer scheduler.AssertExpectations(t) + + jobService := service.NewJobService(nil, nil, nil, nil, nil, nil, nil, nil, nil, scheduler) + err := jobService.UpdateState(ctx, sampleTenant, jobsToUpdateState, state, "job disable remark") + assert.ErrorContains(t, err, "some error in update Job State") + }) + + t.Run("should fail if update state in job table fails", func(t *testing.T) { + scheduler := new(mockScheduler) + scheduler.On("UpdateJobState", ctx, sampleTenant, jobsToUpdateState, state.String()).Return(nil) + defer scheduler.AssertExpectations(t) + + jobRepo := new(JobRepository) + jobRepo.On("UpdateState", ctx, sampleTenant, jobsToUpdateState, state, updateRemark).Return(fmt.Errorf("some error in update Job State repo")) + defer jobRepo.AssertExpectations(t) + + jobService := service.NewJobService(jobRepo, nil, nil, nil, nil, nil, nil, nil, nil, scheduler) + err := jobService.UpdateState(ctx, sampleTenant, jobsToUpdateState, state, updateRemark) + assert.ErrorContains(t, err, "some error in update Job State repo") + }) + + t.Run("should pass when no error in scheduler update and repo update", func(t *testing.T) { + scheduler := new(mockScheduler) + scheduler.On("UpdateJobState", ctx, sampleTenant, jobsToUpdateState, state.String()).Return(nil) + defer scheduler.AssertExpectations(t) + + jobRepo := new(JobRepository) + jobRepo.On("UpdateState", ctx, sampleTenant, jobsToUpdateState, state, updateRemark).Return(nil) + defer jobRepo.AssertExpectations(t) + + eventHandler := newEventHandler(t) + eventHandler.On("HandleEvent", mock.Anything).Times(1) + + jobService := service.NewJobService(jobRepo, nil, nil, nil, nil, nil, eventHandler, nil, nil, scheduler) + err := jobService.UpdateState(ctx, sampleTenant, jobsToUpdateState, state, updateRemark) + assert.Nil(t, err) + }) + }) } // JobRepository is an autogenerated mock type for the JobRepository type @@ -2487,6 +3800,24 @@ func (_m *JobRepository) Delete(ctx context.Context, projectName tenant.ProjectN return r0 } +// ChangeJobNamespace provides a mock function with given fields: ctx, jobName, jobTenant, jobNewTenant +func (_m *JobRepository) ChangeJobNamespace(ctx context.Context, jobName job.Name, jobTenant, jobNewTenant tenant.Tenant) error { + ret := _m.Called(ctx, jobName, jobTenant, jobNewTenant) + return ret.Error(0) +} + +// UpdateState provides a mock function with given fields: ctx, jobName, jobTenant, jobNewTenant +func (_m *JobRepository) UpdateState(ctx context.Context, jobTenant tenant.Tenant, jobNames []job.Name, jobState job.State, remark string) error { + ret := _m.Called(ctx, jobTenant, jobNames, jobState, remark) + return ret.Error(0) +} + +// SyncState provides a mock function with given fields: ctx, jobTenant, disabledJobs, enabledJobs +func (_m *JobRepository) SyncState(ctx context.Context, jobTenant tenant.Tenant, disabledJobNames, enabledJobNames []job.Name) error { + ret := _m.Called(ctx, jobTenant, disabledJobNames, enabledJobNames) + return ret.Error(0) +} + // GetAllByProjectName provides a mock function with given fields: ctx, projectName func (_m *JobRepository) GetAllByProjectName(ctx context.Context, projectName tenant.ProjectName) ([]*job.Job, error) { ret := _m.Called(ctx, projectName) @@ -2579,9 +3910,36 @@ func (_m *JobRepository) GetByJobName(ctx context.Context, projectName tenant.Pr return r0, r1 } +// Update provides a mock function with given fields: _a0, _a1 +func (_m *JobRepository) Update(_a0 context.Context, _a1 []*job.Job) ([]*job.Job, error) { + ret := _m.Called(_a0, _a1) + + var r0 []*job.Job + if rf, ok := ret.Get(0).(func(context.Context, []*job.Job) []*job.Job); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*job.Job) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, []*job.Job) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type DownstreamRepository struct { + mock.Mock +} + // GetDownstreamByDestination provides a mock function with given fields: ctx, projectName, destination -func (_m *JobRepository) GetDownstreamByDestination(ctx context.Context, projectName tenant.ProjectName, destination job.ResourceURN) ([]*job.Downstream, error) { - ret := _m.Called(ctx, projectName, destination) +func (_d *DownstreamRepository) GetDownstreamByDestination(ctx context.Context, projectName tenant.ProjectName, destination job.ResourceURN) ([]*job.Downstream, error) { + ret := _d.Called(ctx, projectName, destination) var r0 []*job.Downstream if rf, ok := ret.Get(0).(func(context.Context, tenant.ProjectName, job.ResourceURN) []*job.Downstream); ok { @@ -2603,8 +3961,8 @@ func (_m *JobRepository) GetDownstreamByDestination(ctx context.Context, project } // GetDownstreamByJobName provides a mock function with given fields: ctx, projectName, jobName -func (_m *JobRepository) GetDownstreamByJobName(ctx context.Context, projectName tenant.ProjectName, jobName job.Name) ([]*job.Downstream, error) { - ret := _m.Called(ctx, projectName, jobName) +func (_d *DownstreamRepository) GetDownstreamByJobName(ctx context.Context, projectName tenant.ProjectName, jobName job.Name) ([]*job.Downstream, error) { + ret := _d.Called(ctx, projectName, jobName) var r0 []*job.Downstream if rf, ok := ret.Get(0).(func(context.Context, tenant.ProjectName, job.Name) []*job.Downstream); ok { @@ -2625,9 +3983,36 @@ func (_m *JobRepository) GetDownstreamByJobName(ctx context.Context, projectName return r0, r1 } +// GetDownstreamBySources provides a mock function with given fields: ctx, sources +func (_d *DownstreamRepository) GetDownstreamBySources(ctx context.Context, sources []job.ResourceURN) ([]*job.Downstream, error) { + ret := _d.Called(ctx, sources) + + var r0 []*job.Downstream + if rf, ok := ret.Get(0).(func(context.Context, []job.ResourceURN) []*job.Downstream); ok { + r0 = rf(ctx, sources) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*job.Downstream) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, []job.ResourceURN) error); ok { + r1 = rf(ctx, sources) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type UpstreamRepository struct { + mock.Mock +} + // GetJobNameWithInternalUpstreams provides a mock function with given fields: _a0, _a1, _a2 -func (_m *JobRepository) ResolveUpstreams(_a0 context.Context, _a1 tenant.ProjectName, _a2 []job.Name) (map[job.Name][]*job.Upstream, error) { - ret := _m.Called(_a0, _a1, _a2) +func (_u *UpstreamRepository) ResolveUpstreams(_a0 context.Context, _a1 tenant.ProjectName, _a2 []job.Name) (map[job.Name][]*job.Upstream, error) { + ret := _u.Called(_a0, _a1, _a2) var r0 map[job.Name][]*job.Upstream if rf, ok := ret.Get(0).(func(context.Context, tenant.ProjectName, []job.Name) map[job.Name][]*job.Upstream); ok { @@ -2649,8 +4034,8 @@ func (_m *JobRepository) ResolveUpstreams(_a0 context.Context, _a1 tenant.Projec } // GetUpstreams provides a mock function with given fields: ctx, projectName, jobName -func (_m *JobRepository) GetUpstreams(ctx context.Context, projectName tenant.ProjectName, jobName job.Name) ([]*job.Upstream, error) { - ret := _m.Called(ctx, projectName, jobName) +func (_u *UpstreamRepository) GetUpstreams(ctx context.Context, projectName tenant.ProjectName, jobName job.Name) ([]*job.Upstream, error) { + ret := _u.Called(ctx, projectName, jobName) var r0 []*job.Upstream if rf, ok := ret.Get(0).(func(context.Context, tenant.ProjectName, job.Name) []*job.Upstream); ok { @@ -2672,8 +4057,8 @@ func (_m *JobRepository) GetUpstreams(ctx context.Context, projectName tenant.Pr } // ReplaceUpstreams provides a mock function with given fields: _a0, _a1 -func (_m *JobRepository) ReplaceUpstreams(_a0 context.Context, _a1 []*job.WithUpstream) error { - ret := _m.Called(_a0, _a1) +func (_u *UpstreamRepository) ReplaceUpstreams(_a0 context.Context, _a1 []*job.WithUpstream) error { + ret := _u.Called(_a0, _a1) var r0 error if rf, ok := ret.Get(0).(func(context.Context, []*job.WithUpstream) error); ok { @@ -2685,29 +4070,6 @@ func (_m *JobRepository) ReplaceUpstreams(_a0 context.Context, _a1 []*job.WithUp return r0 } -// Update provides a mock function with given fields: _a0, _a1 -func (_m *JobRepository) Update(_a0 context.Context, _a1 []*job.Job) ([]*job.Job, error) { - ret := _m.Called(_a0, _a1) - - var r0 []*job.Job - if rf, ok := ret.Get(0).(func(context.Context, []*job.Job) []*job.Job); ok { - r0 = rf(_a0, _a1) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]*job.Job) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, []*job.Job) error); ok { - r1 = rf(_a0, _a1) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // PluginService is an autogenerated mock type for the PluginService type type PluginService struct { mock.Mock @@ -2866,3 +4228,53 @@ type mockWriter struct { func (m *mockWriter) Write(level writer.LogLevel, s string) error { return m.Called(level, s).Error(0) } + +type mockEventHandler struct { + mock.Mock +} + +func (m *mockEventHandler) HandleEvent(e moderator.Event) { + m.Called(e) +} + +type mockConstructorEventHandler interface { + mock.TestingT + Cleanup(func()) +} + +func newEventHandler(t mockConstructorEventHandler) *mockEventHandler { + mock := &mockEventHandler{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// JobDeploymentService is an autogenerated mock type for the JobDeploymentService type +type JobDeploymentService struct { + mock.Mock +} + +// UploadJobs provides a mock function with given fields: ctx, jobTenant, toUpdate, toDelete +func (_m *JobDeploymentService) UploadJobs(ctx context.Context, jobTenant tenant.Tenant, toUpdate, toDelete []string) error { + ret := _m.Called(ctx, jobTenant, toUpdate, toDelete) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, tenant.Tenant, []string, []string) error); ok { + r0 = rf(ctx, jobTenant, toUpdate, toDelete) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +type mockScheduler struct { + mock.Mock +} + +func (ms *mockScheduler) UpdateJobState(ctx context.Context, tnnt tenant.Tenant, jobNames []job.Name, state string) error { + args := ms.Called(ctx, tnnt, jobNames, state) + return args.Error(0) +} diff --git a/core/job/service/plugin_service.go b/core/job/service/plugin_service.go index 5bc31836d0..082cd4bce4 100644 --- a/core/job/service/plugin_service.go +++ b/core/job/service/plugin_service.go @@ -5,13 +5,13 @@ import ( "fmt" "time" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "golang.org/x/net/context" - "github.com/odpf/optimus/core/job" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/compiler" - "github.com/odpf/optimus/sdk/plugin" + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/compiler" + "github.com/raystack/optimus/sdk/plugin" ) const ( @@ -55,10 +55,12 @@ func NewJobPluginService(pluginRepo PluginRepo, engine Engine, logger log.Logger func (p JobPluginService) Info(_ context.Context, taskName job.TaskName) (*plugin.Info, error) { taskPlugin, err := p.pluginRepo.GetByName(taskName.String()) if err != nil { + p.logger.Error("error getting plugin [%s]: %s", taskName.String(), err) return nil, err } if taskPlugin.YamlMod == nil { + p.logger.Error("task plugin yaml mod is not found") return nil, ErrYamlModNotExist } @@ -68,10 +70,12 @@ func (p JobPluginService) Info(_ context.Context, taskName job.TaskName) (*plugi func (p JobPluginService) GenerateDestination(ctx context.Context, tnnt *tenant.WithDetails, task job.Task) (job.ResourceURN, error) { taskPlugin, err := p.pluginRepo.GetByName(task.Name().String()) if err != nil { + p.logger.Error("error getting plugin [%s]: %s", task.Name().String(), err) return "", err } if taskPlugin.DependencyMod == nil { + p.logger.Error(ErrUpstreamModNotFound.Error()) return "", ErrUpstreamModNotFound } @@ -81,6 +85,7 @@ func (p JobPluginService) GenerateDestination(ctx context.Context, tnnt *tenant. Config: compiledConfig, }) if err != nil { + p.logger.Error("error generating destination: %s", err) return "", fmt.Errorf("failed to generate destination: %w", err) } @@ -90,16 +95,19 @@ func (p JobPluginService) GenerateDestination(ctx context.Context, tnnt *tenant. func (p JobPluginService) GenerateUpstreams(ctx context.Context, jobTenant *tenant.WithDetails, spec *job.Spec, dryRun bool) ([]job.ResourceURN, error) { taskPlugin, err := p.pluginRepo.GetByName(spec.Task().Name().String()) if err != nil { + p.logger.Error("error getting plugin [%s]: %s", spec.Task().Name().String(), err) return nil, err } if taskPlugin.DependencyMod == nil { + p.logger.Error(ErrUpstreamModNotFound.Error()) return nil, ErrUpstreamModNotFound } // TODO: this now will always be a same time for start of service, is it correct ? assets, err := p.compileAsset(ctx, taskPlugin, spec, p.now()) if err != nil { + p.logger.Error("error compiling asset: %s", err) return nil, fmt.Errorf("asset compilation failure: %w", err) } @@ -113,6 +121,7 @@ func (p JobPluginService) GenerateUpstreams(ctx context.Context, jobTenant *tena }, }) if err != nil { + p.logger.Error("error generating dependencies: %s", err) return nil, err } @@ -135,7 +144,7 @@ func (p JobPluginService) compileConfig(configs job.Config, tnnt *tenant.WithDet for key, val := range configs { compiledConf, err := p.engine.CompileString(val, tmplCtx) if err != nil { - p.logger.Warn("error in template compilation: ", err.Error()) + p.logger.Warn("template compilation encountered suppressed error: %s", err.Error()) compiledConf = val } pluginConfigs = append(pluginConfigs, plugin.Config{ @@ -161,6 +170,7 @@ func (p JobPluginService) compileAsset(ctx context.Context, taskPlugin *plugin.P }, }) if err != nil { + p.logger.Error("error generating destination: %s", err) return nil, err } jobDestination = jobDestinationResponse.Destination @@ -168,10 +178,12 @@ func (p JobPluginService) compileAsset(ctx context.Context, taskPlugin *plugin.P startTime, err := spec.Window().GetStartTime(scheduledAt) if err != nil { + p.logger.Error("error getting start time: %s", err) return nil, fmt.Errorf("error getting start time: %w", err) } endTime, err := spec.Window().GetEndTime(scheduledAt) if err != nil { + p.logger.Error("error getting end time: %s", err) return nil, fmt.Errorf("error getting end time: %w", err) } @@ -187,6 +199,7 @@ func (p JobPluginService) compileAsset(ctx context.Context, taskPlugin *plugin.P configKeyDestination: jobDestination, }) if err != nil { + p.logger.Error("error compiling asset: %s", err) return nil, fmt.Errorf("failed to compile templates: %w", err) } diff --git a/core/job/service/plugin_service_test.go b/core/job/service/plugin_service_test.go index db85fe7b50..2da1281a53 100644 --- a/core/job/service/plugin_service_test.go +++ b/core/job/service/plugin_service_test.go @@ -5,17 +5,17 @@ import ( "errors" "testing" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/core/job" - "github.com/odpf/optimus/core/job/service" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/compiler" - "github.com/odpf/optimus/internal/models" - "github.com/odpf/optimus/sdk/plugin" - mockOpt "github.com/odpf/optimus/sdk/plugin/mock" + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/core/job/service" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/compiler" + "github.com/raystack/optimus/internal/models" + "github.com/raystack/optimus/sdk/plugin" + mockOpt "github.com/raystack/optimus/sdk/plugin/mock" ) func TestPluginService(t *testing.T) { @@ -52,13 +52,15 @@ func TestPluginService(t *testing.T) { assert.NoError(t, err) jobTask := job.NewTask("bq2bq", jobTaskConfig) + logger := log.NewLogrus() + t.Run("Info", func(t *testing.T) { t.Run("returns error when no plugin", func(t *testing.T) { pluginRepo := new(mockPluginRepo) pluginRepo.On("GetByName", jobTask.Name().String()).Return(nil, errors.New("some error when fetch plugin")) defer pluginRepo.AssertExpectations(t) - pluginService := service.NewJobPluginService(pluginRepo, nil, nil) + pluginService := service.NewJobPluginService(pluginRepo, nil, logger) result, err := pluginService.Info(ctx, jobTask.Name()) assert.Error(t, err) assert.Nil(t, result) @@ -77,7 +79,7 @@ func TestPluginService(t *testing.T) { newPlugin := &plugin.Plugin{DependencyMod: depMod} pluginRepo.On("GetByName", jobTask.Name().String()).Return(newPlugin, nil) - pluginService := service.NewJobPluginService(pluginRepo, nil, nil) + pluginService := service.NewJobPluginService(pluginRepo, nil, logger) result, err := pluginService.Info(ctx, jobTask.Name()) assert.Error(t, err) assert.Nil(t, result) @@ -103,7 +105,7 @@ func TestPluginService(t *testing.T) { }, nil) defer yamlMod.AssertExpectations(t) - pluginService := service.NewJobPluginService(pluginRepo, nil, nil) + pluginService := service.NewJobPluginService(pluginRepo, nil, logger) result, err := pluginService.Info(ctx, jobTask.Name()) assert.NoError(t, err) assert.NotNil(t, result) diff --git a/core/job/spec.go b/core/job/spec.go index 7396d51a10..fe6d3af686 100644 --- a/core/job/spec.go +++ b/core/job/spec.go @@ -5,12 +5,15 @@ import ( "strings" "time" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" - "github.com/odpf/optimus/internal/models" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/models" ) -const DateLayout = "2006-01-02" +const ( + DateLayout = "2006-01-02" + maxJobNameLength = 125 +) type Spec struct { version int @@ -153,12 +156,58 @@ func (s Specs) ToNameAndSpecMap() map[Name]*Spec { return nameAndSpecMap } +func (s Specs) ToFullNameAndSpecMap(projectName tenant.ProjectName) map[FullName]*Spec { + fullnameAndSpecMap := make(map[FullName]*Spec, len(s)) + for _, spec := range s { + fullName := FullNameFrom(projectName, spec.Name()) + fullnameAndSpecMap[fullName] = spec + } + return fullnameAndSpecMap +} + +func (s Specs) Validate() error { + me := errors.NewMultiError("validate specs errors") + jobNameCount := s.getJobNameCount() + isJobNameVisited := map[Name]bool{} + for _, spec := range s { + if jobNameCount[spec.Name()] > 1 && !isJobNameVisited[spec.Name()] { + me.Append(fmt.Errorf("duplicate %s", spec.Name())) + } + isJobNameVisited[spec.Name()] = true + } + + return me.ToErr() +} + +func (s Specs) GetValid() []*Spec { + jobNameCount := s.getJobNameCount() + validSpecs := []*Spec{} + for _, spec := range s { + if jobNameCount[spec.Name()] == 1 { + validSpecs = append(validSpecs, spec) + } + } + + return validSpecs +} + +func (s Specs) getJobNameCount() map[Name]int { + jobNameCount := make(map[Name]int) + for _, spec := range s { + jobNameCount[spec.Name()]++ + } + return jobNameCount +} + type Name string func NameFrom(name string) (Name, error) { if name == "" { return "", errors.InvalidArgument(EntityJob, "name is empty") } + if len(name) > maxJobNameLength { + return "", errors.InvalidArgument(EntityJob, fmt.Sprintf("length of job name is %d, longer than the length allowed (%d)", len(name), maxJobNameLength)) + } return Name(name), nil } @@ -166,6 +215,31 @@ func (n Name) String() string { return string(n) } +type State string + +const ( + ENABLED State = "enabled" + DISABLED State = "disabled" +) + +func StateFrom(name string) (State, error) { + if name == "" { + return "", errors.InvalidArgument(EntityJob, "state is empty") + } + switch name { + case "JOB_STATE_ENABLED": + return ENABLED, nil + case "JOB_STATE_DISABLED": + return DISABLED, nil + default: + return "", errors.InvalidArgument(EntityJob, "invalid state") + } +} + +func (n State) String() string { + return string(n) +} + type ScheduleDate string func ScheduleDateFrom(date string) (ScheduleDate, error) { @@ -210,7 +284,6 @@ type Schedule struct { endDate ScheduleDate interval string dependsOnPast bool - catchUp bool retry *Retry } @@ -230,10 +303,6 @@ func (s Schedule) DependsOnPast() bool { return s.dependsOnPast } -func (s Schedule) CatchUp() bool { - return s.catchUp -} - func (s Schedule) Retry() *Retry { return s.retry } @@ -273,11 +342,6 @@ func (s *ScheduleBuilder) WithDependsOnPast(dependsOnPast bool) *ScheduleBuilder return s } -func (s *ScheduleBuilder) WithCatchUp(catchUp bool) *ScheduleBuilder { - s.schedule.catchUp = catchUp - return s -} - func (s *ScheduleBuilder) WithRetry(retry *Retry) *ScheduleBuilder { s.schedule.retry = retry return s @@ -500,7 +564,7 @@ func (s SpecHTTPUpstream) validate() error { me := errors.NewMultiError("errors on spec http upstream") me.Append(validateMap(s.headers)) me.Append(validateMap(s.params)) - return errors.MultiToError(me) + return me.ToErr() } type SpecHTTPUpstreamBuilder struct { @@ -581,7 +645,7 @@ func (s UpstreamSpec) validate() error { for _, u := range s.httpUpstreams { me.Append(u.validate()) } - return errors.MultiToError(me) + return me.ToErr() } type SpecUpstreamBuilder struct { diff --git a/core/job/spec_test.go b/core/job/spec_test.go index 9466d23e01..122bb5b895 100644 --- a/core/job/spec_test.go +++ b/core/job/spec_test.go @@ -5,9 +5,9 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/job" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/models" + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/models" ) func TestEntitySpec(t *testing.T) { @@ -18,7 +18,6 @@ func TestEntitySpec(t *testing.T) { jobSchedule, _ := job.NewScheduleBuilder(startDate). WithEndDate(endDate). WithInterval("0 2 * * *"). - WithCatchUp(true). WithRetry(retry). WithDependsOnPast(false). Build() @@ -69,7 +68,6 @@ func TestEntitySpec(t *testing.T) { assert.Equal(t, jobSchedule.StartDate(), specA.Schedule().StartDate()) assert.Equal(t, jobSchedule.StartDate().String(), specA.Schedule().StartDate().String()) assert.Equal(t, jobSchedule.DependsOnPast(), specA.Schedule().DependsOnPast()) - assert.Equal(t, jobSchedule.CatchUp(), specA.Schedule().CatchUp()) assert.Equal(t, jobSchedule.Interval(), specA.Schedule().Interval()) assert.Equal(t, jobWindow, specA.Window()) @@ -135,6 +133,57 @@ func TestEntitySpec(t *testing.T) { assert.EqualValues(t, expectedMap, resultMap) }) + t.Run("ToFullNameAndSpecMap should return map with fullname key and spec value", func(t *testing.T) { + projectName := tenant.ProjectName("sample-project") + specA, err := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + assert.NoError(t, err) + + specB, err := job.NewSpecBuilder(jobVersion, "job-B", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + assert.NoError(t, err) + + expectedMap := map[job.FullName]*job.Spec{ + "sample-project/job-A": specA, + "sample-project/job-B": specB, + } + + specs := job.Specs([]*job.Spec{specA, specB}) + resultMap := specs.ToFullNameAndSpecMap(projectName) + + assert.EqualValues(t, expectedMap, resultMap) + }) + + t.Run("Validate should return error for duplicate jobs", func(t *testing.T) { + specA1, err := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + assert.NoError(t, err) + + specA2, err := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + assert.NoError(t, err) + + specB, err := job.NewSpecBuilder(jobVersion, "job-B", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + assert.NoError(t, err) + + specs := job.Specs([]*job.Spec{specA1, specA2, specB}) + err = specs.Validate() + + assert.ErrorContains(t, err, "duplicate job-A") + }) + t.Run("GetValid should return valid specs", func(t *testing.T) { + specA1, err := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + assert.NoError(t, err) + + specA2, err := job.NewSpecBuilder(jobVersion, "job-A", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + assert.NoError(t, err) + + specB, err := job.NewSpecBuilder(jobVersion, "job-B", "sample-owner", jobSchedule, jobWindow, jobTask).Build() + assert.NoError(t, err) + + expectedValidSpecs := []*job.Spec{specB} + + specs := job.Specs([]*job.Spec{specA1, specA2, specB}) + actualValidSpecs := specs.GetValid() + + assert.EqualValues(t, expectedValidSpecs, actualValidSpecs) + }) }) t.Run("SpecUpstreamName", func(t *testing.T) { @@ -218,6 +267,11 @@ func TestEntitySpec(t *testing.T) { assert.ErrorContains(t, err, "name is empty") assert.Empty(t, name) }) + t.Run("should return error if name length is more than maximum allowed", func(t *testing.T) { + name, err := job.NameFrom("QEQFbm'mWufBaUrkccHlFeEDXHaFBOYFRAYGmjwuuTvEhkMekpHVocCBfpX'XBghyFiTTqbYQAseWcfJJsUAdbHRWoWGODoINrglgDsxJjwrmoYRIrGxMAifCGJUqD") + assert.ErrorContains(t, err, "length of job name is 126, longer than the length allowed (125)") + assert.Empty(t, name) + }) }) t.Run("ScheduleDateFrom", func(t *testing.T) { diff --git a/core/resource/backup.go b/core/resource/backup.go index a38fedc2ce..2fc290400f 100644 --- a/core/resource/backup.go +++ b/core/resource/backup.go @@ -5,8 +5,8 @@ import ( "github.com/google/uuid" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" ) const ( diff --git a/core/resource/backup_test.go b/core/resource/backup_test.go index 932e8e14f0..8c8058a957 100644 --- a/core/resource/backup_test.go +++ b/core/resource/backup_test.go @@ -7,8 +7,8 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/tenant" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/tenant" ) func TestBackup(t *testing.T) { diff --git a/core/resource/handler/v1beta1/backup.go b/core/resource/handler/v1beta1/backup.go index 13314f8711..787c9e0f35 100644 --- a/core/resource/handler/v1beta1/backup.go +++ b/core/resource/handler/v1beta1/backup.go @@ -4,13 +4,13 @@ import ( "context" "time" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "google.golang.org/protobuf/types/known/timestamppb" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) type BackupService interface { @@ -29,21 +29,25 @@ type BackupHandler struct { func (b BackupHandler) CreateBackup(ctx context.Context, req *pb.CreateBackupRequest) (*pb.CreateBackupResponse, error) { tnnt, err := tenant.NewTenant(req.GetProjectName(), req.GetNamespaceName()) if err != nil { + b.l.Error("invalid tenant information request project [%s] namespace [%s]: %s", req.GetProjectName(), req.GetNamespaceName(), err) return nil, errors.GRPCErr(err, "invalid backup request") } store, err := resource.FromStringToStore(req.GetDatastoreName()) if err != nil { + b.l.Error("invalid datastore name [%s]: %s", req.GetDatastoreName(), err) return nil, errors.GRPCErr(err, "invalid backup request") } backup, err := resource.NewBackup(store, tnnt, req.ResourceNames, req.Description, time.Now(), req.Config) if err != nil { + b.l.Error("error initializing backup: %s", err) return nil, errors.GRPCErr(err, "invalid backup request") } result, err := b.service.Create(ctx, backup) if err != nil { + b.l.Error("error creating backup: %s", err) return nil, errors.GRPCErr(err, "error during backup") } @@ -57,16 +61,19 @@ func (b BackupHandler) CreateBackup(ctx context.Context, req *pb.CreateBackupReq func (b BackupHandler) ListBackups(ctx context.Context, req *pb.ListBackupsRequest) (*pb.ListBackupsResponse, error) { tnnt, err := tenant.NewTenant(req.GetProjectName(), req.GetNamespaceName()) if err != nil { + b.l.Error("invalid tenant information request project [%s] namespace [%s]: %s", req.GetProjectName(), req.GetNamespaceName(), err) return nil, errors.GRPCErr(err, "invalid list backup request") } store, err := resource.FromStringToStore(req.GetDatastoreName()) if err != nil { + b.l.Error("invalid datastore name [%s]: %s", req.GetDatastoreName(), err) return nil, errors.GRPCErr(err, "invalid list backup request") } results, err := b.service.List(ctx, tnnt, store) if err != nil { + b.l.Error("error listing backups: %s", err) return nil, errors.GRPCErr(err, "error in getting list of backup") } @@ -82,11 +89,13 @@ func (b BackupHandler) ListBackups(ctx context.Context, req *pb.ListBackupsReque func (b BackupHandler) GetBackup(ctx context.Context, req *pb.GetBackupRequest) (*pb.GetBackupResponse, error) { backupID, err := resource.BackupIDFrom(req.GetId()) if err != nil { + b.l.Error("cannot adapt backup id [%s]: %s", req.GetId(), err) return nil, errors.GRPCErr(err, "invalid get backup request") } backupDetail, err := b.service.Get(ctx, backupID) if err != nil { + b.l.Error("error getting backup [%s]: %s", req.GetId(), err) return nil, errors.GRPCErr(err, "invalid get backup request for "+backupID.UUID().String()) } diff --git a/core/resource/handler/v1beta1/backup_test.go b/core/resource/handler/v1beta1/backup_test.go index 76d7e69a37..f955a99543 100644 --- a/core/resource/handler/v1beta1/backup_test.go +++ b/core/resource/handler/v1beta1/backup_test.go @@ -7,14 +7,14 @@ import ( "time" "github.com/google/uuid" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/resource/handler/v1beta1" - "github.com/odpf/optimus/core/tenant" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/resource/handler/v1beta1" + "github.com/raystack/optimus/core/tenant" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) func TestBackupHandler(t *testing.T) { diff --git a/core/resource/handler/v1beta1/resource.go b/core/resource/handler/v1beta1/resource.go index be37c85cee..2cb65e2caf 100644 --- a/core/resource/handler/v1beta1/resource.go +++ b/core/resource/handler/v1beta1/resource.go @@ -7,39 +7,30 @@ import ( "strings" "time" - "github.com/odpf/salt/log" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/raystack/salt/log" "google.golang.org/protobuf/types/known/structpb" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" - "github.com/odpf/optimus/internal/writer" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/telemetry" + "github.com/raystack/optimus/internal/writer" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) -var ( - totalSkippedBatchUpdateGauge = promauto.NewGauge(prometheus.GaugeOpts{ - Name: "resources_batch_update_skipped_total", - Help: "The total number of skipped resources in batch update", - }) - totalSuccessBatchUpdateGauge = promauto.NewGauge(prometheus.GaugeOpts{ - Name: "resources_batch_update_success_total", - Help: "The total number of failure resources in batch update", - }) - totalFailureBatchUpdateGauge = promauto.NewGauge(prometheus.GaugeOpts{ - Name: "resources_batch_update_failure_total", - Help: "The total number of failure resources in batch update", - }) +const ( + metricResourceEvents = "resource_events_total" + metricResourcesUploadAllDuration = "resource_upload_all_duration_seconds_total" ) type ResourceService interface { Create(ctx context.Context, res *resource.Resource) error - Update(ctx context.Context, res *resource.Resource) error + Update(ctx context.Context, res *resource.Resource, logWriter writer.LogWriter) error + ChangeNamespace(ctx context.Context, datastore resource.Store, resourceFullName string, oldTenant, newTenant tenant.Tenant) error Get(ctx context.Context, tnnt tenant.Tenant, store resource.Store, resourceName string) (*resource.Resource, error) GetAll(ctx context.Context, tnnt tenant.Tenant, store resource.Store) ([]*resource.Resource, error) - Deploy(ctx context.Context, tnnt tenant.Tenant, store resource.Store, resources []*resource.Resource) error + Deploy(ctx context.Context, tnnt tenant.Tenant, store resource.Store, resources []*resource.Resource, logWriter writer.LogWriter) error + SyncResources(ctx context.Context, tnnt tenant.Tenant, store resource.Store, names []string) (*resource.SyncResponse, error) } type ResourceHandler struct { @@ -50,7 +41,6 @@ type ResourceHandler struct { } func (rh ResourceHandler) DeployResourceSpecification(stream pb.ResourceService_DeployResourceSpecificationServer) error { - startTime := time.Now() responseWriter := writer.NewDeployResourceSpecificationResponseWriter(stream) var errNamespaces []string @@ -60,12 +50,16 @@ func (rh ResourceHandler) DeployResourceSpecification(stream pb.ResourceService_ if errors.Is(err, io.EOF) { break } + errMsg := fmt.Sprintf("error encountered when receiving stream request: %s", err) + rh.l.Error(errMsg) + responseWriter.Write(writer.LogLevelError, errMsg) return err } + startTime := time.Now() tnnt, err := tenant.NewTenant(request.GetProjectName(), request.GetNamespaceName()) if err != nil { - errMsg := fmt.Sprintf("invalid deploy request for %s: %s", request.GetNamespaceName(), err.Error()) + errMsg := fmt.Sprintf("invalid tenant information request project [%s] namespace [%s]: %s", request.GetProjectName(), request.GetNamespaceName(), err) rh.l.Error(errMsg) responseWriter.Write(writer.LogLevelError, errMsg) errNamespaces = append(errNamespaces, request.NamespaceName) @@ -74,7 +68,7 @@ func (rh ResourceHandler) DeployResourceSpecification(stream pb.ResourceService_ store, err := resource.FromStringToStore(request.GetDatastoreName()) if err != nil { - errMsg := fmt.Sprintf("invalid store name for %s: %s", request.GetDatastoreName(), err.Error()) + errMsg := fmt.Sprintf("invalid store name [%s]: %s", request.GetDatastoreName(), err) rh.l.Error(errMsg) responseWriter.Write(writer.LogLevelError, errMsg) errNamespaces = append(errNamespaces, request.NamespaceName) @@ -85,7 +79,7 @@ func (rh ResourceHandler) DeployResourceSpecification(stream pb.ResourceService_ for _, resourceProto := range request.GetResources() { adapted, err := fromResourceProto(resourceProto, tnnt, store) if err != nil { - errMsg := fmt.Sprintf("%s: cannot adapt resource %s", err.Error(), resourceProto.GetName()) + errMsg := fmt.Sprintf("error adapting resource [%s]: %s", resourceProto.GetName(), err) rh.l.Error(errMsg) responseWriter.Write(writer.LogLevelError, errMsg) continue @@ -96,7 +90,7 @@ func (rh ResourceHandler) DeployResourceSpecification(stream pb.ResourceService_ errNamespaces = append(errNamespaces, request.GetNamespaceName()) } - err = rh.service.Deploy(stream.Context(), tnnt, store, resourceSpecs) + err = rh.service.Deploy(stream.Context(), tnnt, store, resourceSpecs, responseWriter) successResources := getResourcesByStatuses(resourceSpecs, resource.StatusSuccess) skippedResources := getResourcesByStatuses(resourceSpecs, resource.StatusSkipped) failureResources := getResourcesByStatuses(resourceSpecs, resource.StatusCreateFailure, resource.StatusUpdateFailure, resource.StatusValidationFailure) @@ -120,17 +114,23 @@ func (rh ResourceHandler) DeployResourceSpecification(stream pb.ResourceService_ continue } - successMsg := fmt.Sprintf("resources with namespace [%s] are deployed successfully", request.GetNamespaceName()) + successMsg := fmt.Sprintf("[%d] resources with namespace [%s] are deployed successfully", len(resourceSpecs), request.GetNamespaceName()) responseWriter.Write(writer.LogLevelInfo, successMsg) - totalSuccessBatchUpdateGauge.Set(float64(len(successResources))) - totalSkippedBatchUpdateGauge.Set(float64(len(skippedResources))) - totalFailureBatchUpdateGauge.Set(float64(len(failureResources))) + for _, resourceSpec := range resourceSpecs { + raiseResourceDatastoreEventMetric(tnnt, resourceSpec.Store().String(), resourceSpec.Kind(), resourceSpec.Status().String()) + } + + processDuration := time.Since(startTime) + telemetry.NewGauge(metricResourcesUploadAllDuration, map[string]string{ + "project": tnnt.ProjectName().String(), + "namespace": tnnt.NamespaceName().String(), + }).Add(processDuration.Seconds()) } - rh.l.Info("Finished resource deployment in %v", time.Since(startTime)) + if len(errNamespaces) > 0 { namespacesWithError := strings.Join(errNamespaces, ", ") - rh.l.Error("Error while deploying namespaces: [%s]", namespacesWithError) + rh.l.Error("error when deploying namespaces: [%s]", namespacesWithError) return fmt.Errorf("error when deploying: [%s]", namespacesWithError) } return nil @@ -139,16 +139,19 @@ func (rh ResourceHandler) DeployResourceSpecification(stream pb.ResourceService_ func (rh ResourceHandler) ListResourceSpecification(ctx context.Context, req *pb.ListResourceSpecificationRequest) (*pb.ListResourceSpecificationResponse, error) { store, err := resource.FromStringToStore(req.GetDatastoreName()) if err != nil { + rh.l.Error("invalid store name [%s]: %s", req.GetDatastoreName(), err) return nil, errors.GRPCErr(err, "invalid list resource request") } tnnt, err := tenant.NewTenant(req.GetProjectName(), req.GetNamespaceName()) if err != nil { + rh.l.Error("invalid tenant information request project [%s] namespace [%s]: %s", req.GetProjectName(), req.GetNamespaceName(), err) return nil, errors.GRPCErr(err, "failed to list resource for "+req.GetDatastoreName()) } resources, err := rh.service.GetAll(ctx, tnnt, store) if err != nil { + rh.l.Error("error getting all resources: %s", err) return nil, errors.GRPCErr(err, "failed to list resource for "+req.GetDatastoreName()) } @@ -156,6 +159,7 @@ func (rh ResourceHandler) ListResourceSpecification(ctx context.Context, req *pb for _, resourceSpec := range resources { resourceProto, err := toResourceProto(resourceSpec) if err != nil { + rh.l.Error("error adapting resource [%s]: %s", resourceSpec.FullName(), err) return nil, errors.GRPCErr(err, "failed to parse resource "+resourceSpec.FullName()) } resourceProtos = append(resourceProtos, resourceProto) @@ -169,48 +173,59 @@ func (rh ResourceHandler) ListResourceSpecification(ctx context.Context, req *pb func (rh ResourceHandler) CreateResource(ctx context.Context, req *pb.CreateResourceRequest) (*pb.CreateResourceResponse, error) { tnnt, err := tenant.NewTenant(req.GetProjectName(), req.GetNamespaceName()) if err != nil { + rh.l.Error("invalid tenant information request project [%s] namespace [%s]: %s", req.GetProjectName(), req.GetNamespaceName(), err) return nil, errors.GRPCErr(err, "failed to create resource") } store, err := resource.FromStringToStore(req.GetDatastoreName()) if err != nil { + rh.l.Error("invalid datastore name [%s]: %s", req.GetDatastoreName(), err) return nil, errors.GRPCErr(err, "invalid create resource request") } res, err := fromResourceProto(req.Resource, tnnt, store) if err != nil { + rh.l.Error("error adapting resource [%s]: %s", req.GetResource().GetName(), err) return nil, errors.GRPCErr(err, "failed to create resource") } err = rh.service.Create(ctx, res) + raiseResourceDatastoreEventMetric(tnnt, res.Store().String(), res.Kind(), res.Status().String()) if err != nil { + rh.l.Error("error creating resource [%s]: %s", res.FullName(), err) return nil, errors.GRPCErr(err, "failed to create resource "+res.FullName()) } + return &pb.CreateResourceResponse{}, nil } func (rh ResourceHandler) ReadResource(ctx context.Context, req *pb.ReadResourceRequest) (*pb.ReadResourceResponse, error) { if req.GetResourceName() == "" { + rh.l.Error("resource name is empty") return nil, errors.GRPCErr(errors.InvalidArgument(resource.EntityResource, "empty resource name"), "invalid read resource request") } store, err := resource.FromStringToStore(req.GetDatastoreName()) if err != nil { + rh.l.Error("invalid datastore name [%s]: %s", req.GetDatastoreName(), err) return nil, errors.GRPCErr(err, "invalid read resource request") } tnnt, err := tenant.NewTenant(req.GetProjectName(), req.GetNamespaceName()) if err != nil { + rh.l.Error("invalid tenant information request project [%s] namespace [%s]: %s", req.GetProjectName(), req.GetNamespaceName(), err) return nil, errors.GRPCErr(err, "failed to read resource "+req.GetResourceName()) } response, err := rh.service.Get(ctx, tnnt, store, req.GetResourceName()) if err != nil { + rh.l.Error("error getting resource [%s]: %s", req.GetResourceName(), err) return nil, errors.GRPCErr(err, "failed to read resource "+req.GetResourceName()) } protoResource, err := toResourceProto(response) if err != nil { + rh.l.Error("error adapting resource [%s]: %s", req.GetResourceName(), err) return nil, errors.GRPCErr(err, "failed to read resource "+req.GetResourceName()) } @@ -222,26 +237,101 @@ func (rh ResourceHandler) ReadResource(ctx context.Context, req *pb.ReadResource func (rh ResourceHandler) UpdateResource(ctx context.Context, req *pb.UpdateResourceRequest) (*pb.UpdateResourceResponse, error) { tnnt, err := tenant.NewTenant(req.GetProjectName(), req.GetNamespaceName()) if err != nil { + rh.l.Error("invalid tenant information request project [%s] namespace [%s]: %s", req.GetProjectName(), req.GetNamespaceName(), err) return nil, errors.GRPCErr(err, "failed to update resource") } store, err := resource.FromStringToStore(req.GetDatastoreName()) if err != nil { + rh.l.Error("invalid datastore name [%s]: %s", req.GetDatastoreName(), err) return nil, errors.GRPCErr(err, "invalid update resource request") } res, err := fromResourceProto(req.Resource, tnnt, store) if err != nil { + rh.l.Error("error adapting resource [%s]: %s", req.GetResource().GetName(), err) return nil, errors.GRPCErr(err, "failed to update resource") } - err = rh.service.Update(ctx, res) + logWriter := writer.NewLogWriter(rh.l) + + err = rh.service.Update(ctx, res, logWriter) + raiseResourceDatastoreEventMetric(tnnt, res.Store().String(), res.Kind(), res.Status().String()) if err != nil { + rh.l.Error("error updating resource [%s]: %s", res.FullName(), err) return nil, errors.GRPCErr(err, "failed to update resource "+res.FullName()) } + return &pb.UpdateResourceResponse{}, nil } +func (rh ResourceHandler) ChangeResourceNamespace(ctx context.Context, req *pb.ChangeResourceNamespaceRequest) (*pb.ChangeResourceNamespaceResponse, error) { + tnnt, err := tenant.NewTenant(req.GetProjectName(), req.GetNamespaceName()) + if err != nil { + return nil, errors.GRPCErr(err, "failed to adapt to existing tenant details") + } + + newTnnt, err := tenant.NewTenant(req.GetProjectName(), req.GetNewNamespaceName()) + if err != nil { + return nil, errors.GRPCErr(err, "failed to adapt to new tenant") + } + + store, err := resource.FromStringToStore(req.GetDatastoreName()) + if err != nil { + return nil, errors.GRPCErr(err, "invalid Datastore Name") + } + + err = rh.service.ChangeNamespace(ctx, store, req.GetResourceName(), tnnt, newTnnt) + if err != nil { + return nil, errors.GRPCErr(err, "failed to update resource "+req.GetResourceName()) + } + + telemetry.NewCounter("resource_namespace_migrations_total", map[string]string{ + "project": tnnt.ProjectName().String(), + "namespace_source": tnnt.NamespaceName().String(), + "namespace_destination": newTnnt.NamespaceName().String(), + }).Inc() + + return &pb.ChangeResourceNamespaceResponse{}, nil +} + +func (rh ResourceHandler) ApplyResources(ctx context.Context, req *pb.ApplyResourcesRequest) (*pb.ApplyResourcesResponse, error) { + tnnt, err := tenant.NewTenant(req.GetProjectName(), req.GetNamespaceName()) + if err != nil { + return nil, errors.GRPCErr(err, "invalid tenant details") + } + + store, err := resource.FromStringToStore(req.GetDatastoreName()) + if err != nil { + return nil, errors.GRPCErr(err, "invalid datastore Name") + } + + if len(req.ResourceNames) == 0 { + return nil, errors.GRPCErr(errors.InvalidArgument(resource.EntityResource, "empty resource names"), "unable to apply resources") + } + + statuses, err := rh.service.SyncResources(ctx, tnnt, store, req.ResourceNames) + if err != nil { + return nil, errors.GRPCErr(err, "unable to sync to datastore") + } + + var respStatuses []*pb.ApplyResourcesResponse_ResourceStatus + for _, r := range statuses.ResourceNames { + respStatuses = append(respStatuses, &pb.ApplyResourcesResponse_ResourceStatus{ + ResourceName: r, + Status: "success", + }) + } + for _, r := range statuses.IgnoredResources { + respStatuses = append(respStatuses, &pb.ApplyResourcesResponse_ResourceStatus{ + ResourceName: r.Name, + Status: "failure", + Reason: r.Reason, + }) + } + return &pb.ApplyResourcesResponse{Statuses: respStatuses}, nil +} + func writeError(logWriter writer.LogWriter, err error) { if err == nil { return @@ -331,6 +421,16 @@ func toResourceProto(res *resource.Resource) (*pb.ResourceSpecification, error) }, nil } +func raiseResourceDatastoreEventMetric(jobTenant tenant.Tenant, datastoreName, resourceKind, state string) { + telemetry.NewCounter(metricResourceEvents, map[string]string{ + "project": jobTenant.ProjectName().String(), + "namespace": jobTenant.NamespaceName().String(), + "datastore": datastoreName, + "type": resourceKind, + "status": state, + }).Inc() +} + func NewResourceHandler(l log.Logger, resourceService ResourceService) *ResourceHandler { return &ResourceHandler{ l: l, diff --git a/core/resource/handler/v1beta1/resource_test.go b/core/resource/handler/v1beta1/resource_test.go index 99ff738089..69afb75abc 100644 --- a/core/resource/handler/v1beta1/resource_test.go +++ b/core/resource/handler/v1beta1/resource_test.go @@ -6,16 +6,17 @@ import ( "io" "testing" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "google.golang.org/grpc/metadata" "google.golang.org/protobuf/types/known/structpb" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/resource/handler/v1beta1" - "github.com/odpf/optimus/core/tenant" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/resource/handler/v1beta1" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/writer" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) func TestResourceHandler(t *testing.T) { @@ -31,6 +32,7 @@ func TestResourceHandler(t *testing.T) { stream := new(resourceStreamMock) stream.On("Context").Return(ctx) stream.On("Recv").Return(nil, errors.New("req timeout")).Once() + stream.On("Send", mock.Anything).Return(nil) err := handler.DeployResourceSpecification(stream) assert.NotNil(t, err) @@ -48,7 +50,7 @@ func TestResourceHandler(t *testing.T) { } argMatcher := mock.MatchedBy(func(req *pb.DeployResourceSpecificationResponse) bool { - return req.LogStatus.Message == "invalid deploy request for ns: invalid argument for entity project: project name is empty" + return req.LogStatus.Message == "invalid tenant information request project [] namespace [ns]: invalid argument for entity project: project name is empty" }) stream := new(resourceStreamMock) stream.On("Context").Return(ctx) @@ -72,7 +74,7 @@ func TestResourceHandler(t *testing.T) { } argMatcher := mock.MatchedBy(func(req *pb.DeployResourceSpecificationResponse) bool { - return req.LogStatus.Message == "invalid store name for unknown: invalid argument for entity resource: unknown store unknown" + return req.LogStatus.Message == "invalid store name [unknown]: invalid argument for entity resource: unknown store unknown" }) stream := new(resourceStreamMock) stream.On("Context").Return(ctx) @@ -86,8 +88,7 @@ func TestResourceHandler(t *testing.T) { }) t.Run("returns error log when conversion fails", func(t *testing.T) { service := new(resourceService) - service.On("Deploy", ctx, mock.Anything, resource.Bigquery, mock.Anything). - Return(nil) + service.On("Deploy", ctx, mock.Anything, resource.Bigquery, mock.Anything, mock.Anything).Return(nil) defer service.AssertExpectations(t) handler := v1beta1.NewResourceHandler(logger, service) @@ -186,7 +187,7 @@ func TestResourceHandler(t *testing.T) { } argMatcher := mock.MatchedBy(func(req *pb.DeployResourceSpecificationResponse) bool { - return req.LogStatus.Message == "resources with namespace [ns] are deployed successfully" + return req.LogStatus.Message == "[1] resources with namespace [ns] are deployed successfully" }) stream := new(resourceStreamMock) stream.On("Context").Return(ctx) @@ -665,7 +666,7 @@ func TestResourceHandler(t *testing.T) { }) t.Run("returns error when service returns error", func(t *testing.T) { service := new(resourceService) - service.On("Update", ctx, mock.Anything).Return(errors.New("validation failure")) + service.On("Update", ctx, mock.Anything, mock.Anything).Return(errors.New("validation failure")) defer service.AssertExpectations(t) handler := v1beta1.NewResourceHandler(logger, service) @@ -690,7 +691,7 @@ func TestResourceHandler(t *testing.T) { }) t.Run("updates the resource successfully", func(t *testing.T) { service := new(resourceService) - service.On("Update", ctx, mock.Anything).Return(nil) + service.On("Update", ctx, mock.Anything, mock.Anything).Return(nil) defer service.AssertExpectations(t) handler := v1beta1.NewResourceHandler(logger, service) @@ -712,6 +713,100 @@ func TestResourceHandler(t *testing.T) { assert.Nil(t, err) }) }) + t.Run("ApplyResource", func(t *testing.T) { + t.Run("returns error when tenant is invalid", func(t *testing.T) { + service := new(resourceService) + handler := v1beta1.NewResourceHandler(logger, service) + + req := &pb.ApplyResourcesRequest{ + ProjectName: "", + DatastoreName: "bigquery", + ResourceNames: nil, + NamespaceName: "", + } + + _, err := handler.ApplyResources(ctx, req) + assert.NotNil(t, err) + assert.EqualError(t, err, "rpc error: code = InvalidArgument desc = invalid argument for entity "+ + "project: project name is empty: invalid tenant details") + }) + t.Run("returns error when store is invalid", func(t *testing.T) { + service := new(resourceService) + handler := v1beta1.NewResourceHandler(logger, service) + + req := &pb.ApplyResourcesRequest{ + ProjectName: "proj", + DatastoreName: "", + ResourceNames: nil, + NamespaceName: "ns", + } + + _, err := handler.ApplyResources(ctx, req) + assert.NotNil(t, err) + assert.EqualError(t, err, "rpc error: code = InvalidArgument desc = invalid argument for entity "+ + "resource: unknown store : invalid datastore Name") + }) + t.Run("returns error when resource names are empty", func(t *testing.T) { + service := new(resourceService) + handler := v1beta1.NewResourceHandler(logger, service) + + req := &pb.ApplyResourcesRequest{ + ProjectName: "proj", + DatastoreName: "bigquery", + ResourceNames: nil, + NamespaceName: "ns", + } + + _, err := handler.ApplyResources(ctx, req) + assert.NotNil(t, err) + assert.EqualError(t, err, "rpc error: code = InvalidArgument desc = invalid argument for entity "+ + "resource: empty resource names: unable to apply resources") + }) + t.Run("returns error when service returns error", func(t *testing.T) { + names := []string{"project.dataset.test_table"} + + service := new(resourceService) + service.On("SyncResources", ctx, tnnt, resource.Bigquery, names).Return(nil, errors.New("something went wrong")) + defer service.AssertExpectations(t) + + handler := v1beta1.NewResourceHandler(logger, service) + + req := &pb.ApplyResourcesRequest{ + ProjectName: "proj", + NamespaceName: "ns", + DatastoreName: "bigquery", + ResourceNames: names, + } + + _, err := handler.ApplyResources(ctx, req) + assert.NotNil(t, err) + assert.EqualError(t, err, "rpc error: code = Internal desc = something went wrong: "+ + "unable to sync to datastore") + }) + t.Run("syncs the resources successfully", func(t *testing.T) { + names := []string{"project.dataset.test_table"} + + service := new(resourceService) + service.On("SyncResources", ctx, tnnt, resource.Bigquery, names).Return( + &resource.SyncResponse{ResourceNames: names}, nil) + defer service.AssertExpectations(t) + + handler := v1beta1.NewResourceHandler(logger, service) + + req := &pb.ApplyResourcesRequest{ + ProjectName: "proj", + NamespaceName: "ns", + DatastoreName: "bigquery", + ResourceNames: names, + } + + resp, err := handler.ApplyResources(ctx, req) + assert.Nil(t, err) + + assert.Equal(t, "success", resp.Statuses[0].Status) + assert.Equal(t, names[0], resp.Statuses[0].ResourceName) + }) + }) } type resourceService struct { @@ -723,8 +818,8 @@ func (r *resourceService) Create(ctx context.Context, res *resource.Resource) er return args.Error(0) } -func (r *resourceService) Update(ctx context.Context, res *resource.Resource) error { - args := r.Called(ctx, res) +func (r *resourceService) Update(ctx context.Context, res *resource.Resource, logWriter writer.LogWriter) error { + args := r.Called(ctx, res, logWriter) return args.Error(0) } @@ -746,11 +841,24 @@ func (r *resourceService) GetAll(ctx context.Context, tnnt tenant.Tenant, store return resources, args.Error(1) } -func (r *resourceService) Deploy(ctx context.Context, tnnt tenant.Tenant, store resource.Store, resources []*resource.Resource) error { - args := r.Called(ctx, tnnt, store, resources) +func (r *resourceService) Deploy(ctx context.Context, tnnt tenant.Tenant, store resource.Store, resources []*resource.Resource, logWriter writer.LogWriter) error { + args := r.Called(ctx, tnnt, store, resources, logWriter) return args.Error(0) } +func (r *resourceService) ChangeNamespace(ctx context.Context, datastore resource.Store, resourceFullName string, oldTenant, newTenant tenant.Tenant) error { + return r.Called(ctx, datastore, resourceFullName, oldTenant, newTenant).Error(0) +} + +func (r *resourceService) SyncResources(ctx context.Context, tnnt tenant.Tenant, store resource.Store, names []string) (*resource.SyncResponse, error) { + args := r.Called(ctx, tnnt, store, names) + var resources *resource.SyncResponse + if args.Get(0) != nil { + resources = args.Get(0).(*resource.SyncResponse) + } + return resources, args.Error(1) +} + type resourceStreamMock struct { mock.Mock } diff --git a/core/resource/resource.go b/core/resource/resource.go index 91e1dfaf50..51d500325b 100644 --- a/core/resource/resource.go +++ b/core/resource/resource.go @@ -4,8 +4,8 @@ import ( "reflect" "strings" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" ) const ( @@ -94,6 +94,10 @@ func (r *Resource) UpdateURN(urn string) error { return errors.InvalidArgument(EntityResource, "urn already present for "+r.FullName()) } +func (r *Resource) UpdateTenant(tnnt tenant.Tenant) { + r.tenant = tnnt +} + func (r *Resource) Metadata() *Metadata { return r.metadata } diff --git a/core/resource/resource_status.go b/core/resource/resource_status.go index d36ab38e65..4d7dbb1f16 100644 --- a/core/resource/resource_status.go +++ b/core/resource/resource_status.go @@ -3,7 +3,7 @@ package resource import ( "fmt" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/internal/errors" ) func (r *Resource) MarkValidationSuccess() error { diff --git a/core/resource/resource_test.go b/core/resource/resource_test.go index adda8b4daa..1054187b96 100644 --- a/core/resource/resource_test.go +++ b/core/resource/resource_test.go @@ -5,8 +5,8 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/tenant" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/tenant" ) func TestName(t *testing.T) { diff --git a/core/resource/service/backup_service.go b/core/resource/service/backup_service.go index b0d3c04f17..e96d212e20 100644 --- a/core/resource/service/backup_service.go +++ b/core/resource/service/backup_service.go @@ -2,15 +2,25 @@ package service import ( "context" + "strings" "time" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/salt/log" + + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/telemetry" ) -// recentBackupWindowMonths contains the window interval to consider for recent backups -const recentBackupWindowMonths = -3 +const ( + // recentBackupWindowMonths contains the window interval to consider for recent backups + recentBackupWindowMonths = -3 + + metricBackupRequest = "resource_backup_requests_total" + backupRequestStatusSuccess = "success" + backupRequestStatusFailed = "failed" +) type BackupRepository interface { GetByID(ctx context.Context, id resource.BackupID) (*resource.Backup, error) @@ -31,32 +41,40 @@ type BackupService struct { resources ResourceProvider backupManager BackupManager + + logger log.Logger } func (s BackupService) Create(ctx context.Context, backup *resource.Backup) (*resource.BackupResult, error) { resources, err := s.resources.GetResources(ctx, backup.Tenant(), backup.Store(), backup.ResourceNames()) if err != nil { + s.logger.Error("error getting resources [%s] from db: %s", strings.Join(backup.ResourceNames(), ", "), err) return nil, err } ignored := findMissingResources(backup.ResourceNames(), resources) backupInfo, err := s.backupManager.Backup(ctx, backup, resources) if err != nil { + s.logger.Error("error backup up through manager: %s", err) return nil, err } backupInfo.IgnoredResources = append(backupInfo.IgnoredResources, ignored...) err = s.repo.Create(ctx, backup) if err != nil { + s.logger.Error("error creating backup record to db: %s", err) return backupInfo, err } + raiseBackupRequestMetrics(backup.Tenant(), backupInfo) + backupInfo.ID = backup.ID() return backupInfo, nil } func (s BackupService) Get(ctx context.Context, backupID resource.BackupID) (*resource.Backup, error) { if backupID.IsInvalid() { + s.logger.Error("backup id [%s] is invalid", backupID.String()) return nil, errors.InvalidArgument("backup", "the backup id is not valid") } return s.repo.GetByID(ctx, backupID) @@ -65,6 +83,7 @@ func (s BackupService) Get(ctx context.Context, backupID resource.BackupID) (*re func (s BackupService) List(ctx context.Context, tnnt tenant.Tenant, store resource.Store) ([]*resource.Backup, error) { backups, err := s.repo.GetAll(ctx, tnnt, store) if err != nil { + s.logger.Error("error getting all backups from db: %s", err) return nil, err } @@ -101,10 +120,29 @@ func findMissingResources(names []string, resources []*resource.Resource) []reso return ignored } -func NewBackupService(repo BackupRepository, resources ResourceProvider, manager BackupManager) *BackupService { +func NewBackupService(repo BackupRepository, resources ResourceProvider, manager BackupManager, logger log.Logger) *BackupService { return &BackupService{ repo: repo, resources: resources, backupManager: manager, + logger: logger, } } + +func raiseBackupRequestMetrics(jobTenant tenant.Tenant, backupResult *resource.BackupResult) { + for _, ignoredResource := range backupResult.IgnoredResources { + raiseBackupRequestMetric(jobTenant, ignoredResource.Name, backupRequestStatusFailed) + } + for _, resourceName := range backupResult.ResourceNames { + raiseBackupRequestMetric(jobTenant, resourceName, backupRequestStatusSuccess) + } +} + +func raiseBackupRequestMetric(jobTenant tenant.Tenant, resourceName, state string) { + telemetry.NewCounter(metricBackupRequest, map[string]string{ + "project": jobTenant.ProjectName().String(), + "namespace": jobTenant.NamespaceName().String(), + "resource": resourceName, + "status": state, + }).Inc() +} diff --git a/core/resource/service/backup_service_test.go b/core/resource/service/backup_service_test.go index 5b5afe4d67..08683fe1c3 100644 --- a/core/resource/service/backup_service_test.go +++ b/core/resource/service/backup_service_test.go @@ -6,13 +6,14 @@ import ( "time" "github.com/google/uuid" + "github.com/raystack/salt/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/resource/service" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/resource/service" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" ) func TestBackupService(t *testing.T) { @@ -29,6 +30,8 @@ func TestBackupService(t *testing.T) { validID := "dda7b864-4268-4107-a096-dcf5343a0959" id, _ := uuid.Parse(validID) + logger := log.NewLogrus() + t.Run("Create", func(t *testing.T) { t.Run("returns error when cannot get resources", func(t *testing.T) { resourceProvider := new(mockResourceProvider) @@ -36,7 +39,7 @@ func TestBackupService(t *testing.T) { Return(nil, errors.InternalError("repo", "cannot get", nil)) defer resourceProvider.AssertExpectations(t) - backupService := service.NewBackupService(nil, resourceProvider, nil) + backupService := service.NewBackupService(nil, resourceProvider, nil, logger) _, err := backupService.Create(ctx, backup) assert.Error(t, err) assert.EqualError(t, err, "internal error for entity repo: cannot get") @@ -53,7 +56,7 @@ func TestBackupService(t *testing.T) { Return(nil, errors.InternalError("bq", "something wrong", nil)) defer backupManager.AssertExpectations(t) - backupService := service.NewBackupService(nil, resourceProvider, backupManager) + backupService := service.NewBackupService(nil, resourceProvider, backupManager, logger) _, err := backupService.Create(ctx, backup) assert.Error(t, err) assert.EqualError(t, err, "internal error for entity bq: something wrong") @@ -74,7 +77,7 @@ func TestBackupService(t *testing.T) { repo.On("Create", ctx, backup).Return(errors.InternalError("repo", "cannot save", nil)) defer repo.AssertExpectations(t) - backupService := service.NewBackupService(repo, resourceProvider, backupManager) + backupService := service.NewBackupService(repo, resourceProvider, backupManager, logger) _, err := backupService.Create(ctx, backup) assert.Error(t, err) assert.EqualError(t, err, "internal error for entity repo: cannot save") @@ -95,7 +98,7 @@ func TestBackupService(t *testing.T) { repo.On("Create", ctx, backup).Return(nil) defer repo.AssertExpectations(t) - backupService := service.NewBackupService(repo, resourceProvider, backupManager) + backupService := service.NewBackupService(repo, resourceProvider, backupManager, logger) result, err := backupService.Create(ctx, backup) assert.NoError(t, err) assert.Equal(t, "p.d.t", result.ResourceNames[0]) @@ -119,7 +122,7 @@ func TestBackupService(t *testing.T) { repo.On("Create", ctx, bk1).Return(nil) defer repo.AssertExpectations(t) - backupService := service.NewBackupService(repo, resourceProvider, backupManager) + backupService := service.NewBackupService(repo, resourceProvider, backupManager, logger) result, err := backupService.Create(ctx, bk1) assert.NoError(t, err) assert.Equal(t, "p.d.t", result.ResourceNames[0]) @@ -133,7 +136,7 @@ func TestBackupService(t *testing.T) { id := uuid.Nil backupID := resource.BackupID(id) - backupService := service.NewBackupService(nil, nil, nil) + backupService := service.NewBackupService(nil, nil, nil, logger) _, err := backupService.Get(ctx, backupID) assert.Error(t, err) assert.EqualError(t, err, "invalid argument for entity backup: the backup id is not valid") @@ -144,7 +147,7 @@ func TestBackupService(t *testing.T) { repo.On("GetByID", ctx, backupID).Return(backup, nil) defer repo.AssertExpectations(t) - backupService := service.NewBackupService(repo, nil, nil) + backupService := service.NewBackupService(repo, nil, nil, logger) bkup, err := backupService.Get(ctx, backupID) assert.NoError(t, err) assert.Equal(t, "p.d.t", bkup.ResourceNames()[0]) @@ -156,7 +159,7 @@ func TestBackupService(t *testing.T) { repo.On("GetAll", ctx, tnnt, store).Return(nil, errors.NotFound("backup", "error in list backups")) defer repo.AssertExpectations(t) - backupService := service.NewBackupService(repo, nil, nil) + backupService := service.NewBackupService(repo, nil, nil, logger) _, err := backupService.List(ctx, tnnt, store) assert.Error(t, err) assert.EqualError(t, err, "not found for entity backup: error in list backups") @@ -175,7 +178,7 @@ func TestBackupService(t *testing.T) { repo.On("GetAll", ctx, tnnt, store).Return([]*resource.Backup{bk1, bk2}, nil) defer repo.AssertExpectations(t) - backupService := service.NewBackupService(repo, nil, nil) + backupService := service.NewBackupService(repo, nil, nil, logger) lst, err := backupService.List(ctx, tnnt, store) assert.NoError(t, err) diff --git a/core/resource/service/resource_manager.go b/core/resource/service/resource_manager.go index 06e52266f9..fc19530dab 100644 --- a/core/resource/service/resource_manager.go +++ b/core/resource/service/resource_manager.go @@ -4,10 +4,10 @@ import ( "context" "fmt" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/internal/errors" ) type DataStore interface { @@ -48,14 +48,14 @@ func (m *ResourceMgr) CreateResource(ctx context.Context, res *resource.Resource me.Append(res.MarkExistInStore()) } else { me.Append(res.MarkFailure()) + me.Append(err) } - me.Append(err) } else { me.Append(res.MarkSuccess()) } me.Append(m.repo.UpdateStatus(ctx, res)) - return errors.MultiToError(me) + return me.ToErr() } func (m *ResourceMgr) UpdateResource(ctx context.Context, res *resource.Resource) error { @@ -77,7 +77,32 @@ func (m *ResourceMgr) UpdateResource(ctx context.Context, res *resource.Resource } me.Append(m.repo.UpdateStatus(ctx, res)) - return errors.MultiToError(me) + return me.ToErr() +} + +func (m *ResourceMgr) SyncResource(ctx context.Context, res *resource.Resource) error { + store := res.Store() + datastore, ok := m.datastoreMap[store] + if !ok { + msg := fmt.Sprintf("datastore [%s] for resource [%s] is not found", store.String(), res.FullName()) + m.logger.Error(msg) + return errors.InternalError(resource.EntityResource, msg, nil) + } + + if err := datastore.Create(ctx, res); err != nil { + if !errors.IsErrorType(err, errors.ErrAlreadyExists) { + return errors.AddErrContext(err, resource.EntityResource, "unable to create on datastore") + } else if errUpdate := datastore.Update(ctx, res); errUpdate != nil { + return errors.AddErrContext(errUpdate, resource.EntityResource, "unable to update on datastore") + } + } + + resNew := resource.FromExisting(res, resource.ReplaceStatus(resource.StatusSuccess)) + if err := m.repo.UpdateStatus(ctx, resNew); err != nil { + return errors.AddErrContext(err, resource.EntityResource, "unable to update status in database") + } + + return nil } func (m *ResourceMgr) Validate(res *resource.Resource) error { @@ -111,16 +136,17 @@ func (m *ResourceMgr) BatchUpdate(ctx context.Context, store resource.Store, res return errors.InvalidArgument(resource.EntityResource, "data store service not found for "+store.String()) } - err := errors.NewMultiError("error in batch update") - err.Append(datastore.BatchUpdate(ctx, resources)) - err.Append(m.repo.UpdateStatus(ctx, resources...)) + me := errors.NewMultiError("error in batch update") + me.Append(datastore.BatchUpdate(ctx, resources)) + me.Append(m.repo.UpdateStatus(ctx, resources...)) - return errors.MultiToError(err) + return me.ToErr() } func (m *ResourceMgr) Backup(ctx context.Context, details *resource.Backup, resources []*resource.Resource) (*resource.BackupResult, error) { datastore, ok := m.datastoreMap[details.Store()] if !ok { + m.logger.Error("datastore [%s] is not found", details.Store()) return nil, errors.InvalidArgument(resource.EntityResource, "data store service not found for "+details.Store().String()) } diff --git a/core/resource/service/resource_manager_test.go b/core/resource/service/resource_manager_test.go index be3f8b563b..e3a564e456 100644 --- a/core/resource/service/resource_manager_test.go +++ b/core/resource/service/resource_manager_test.go @@ -5,14 +5,14 @@ import ( "testing" "time" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/resource/service" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/resource/service" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" ) func TestResourceManager(t *testing.T) { @@ -115,7 +115,7 @@ func TestResourceManager(t *testing.T) { manager.RegisterDatastore(store, storeService) err = manager.CreateResource(ctx, createRequest) - assert.Error(t, err) + assert.NoError(t, err) }) t.Run("creates the resource on the datastore", func(t *testing.T) { spec := map[string]any{"description": "test spec"} @@ -448,6 +448,129 @@ func TestResourceManager(t *testing.T) { assert.Equal(t, "proj.ds.name1", result.ResourceNames[0]) }) }) + t.Run("SyncResource", func(t *testing.T) { + t.Run("returns error when store name is invalid", func(t *testing.T) { + repo := new(mockRepo) + logger := log.NewLogrus() + manager := service.NewResourceManager(repo, logger) + + spec := map[string]any{"description": "test spec"} + res, err := resource.NewResource("proj.ds.name1", "table", store, tnnt, meta, spec) + assert.Nil(t, err) + + err = manager.SyncResource(ctx, res) + assert.NotNil(t, err) + assert.EqualError(t, err, "internal error for entity resource: datastore [snowflake] for resource [proj.ds.name1] is not found") + }) + t.Run("returns error when create fails", func(t *testing.T) { + spec := map[string]any{"description": "test spec"} + res, err := resource.NewResource("proj.ds.name1", "table", store, tnnt, meta, spec) + assert.Nil(t, err) + + argMatcher := mock.MatchedBy(func(r []*resource.Resource) bool { + if len(r) != 1 { + return false + } + return r[0].Name() == res.Name() + }) + repo := new(mockRepo) + repo.On("UpdateStatus", ctx, argMatcher).Return(nil) + logger := log.NewLogrus() + manager := service.NewResourceManager(repo, logger) + + storeService := new(mockDataStore) + storeService.On("Create", ctx, res).Return(errors.InternalError("resource", "error in create", nil)) + defer storeService.AssertExpectations(t) + + manager.RegisterDatastore(store, storeService) + + err = manager.SyncResource(ctx, res) + assert.NotNil(t, err) + assert.EqualError(t, err, "internal error for entity resource: unable to create on datastore: "+ + "internal error for entity resource: error in create") + }) + t.Run("returns error when update fails", func(t *testing.T) { + spec := map[string]any{"description": "test spec"} + res, err := resource.NewResource("proj.ds.name1", "table", store, tnnt, meta, spec) + assert.Nil(t, err) + + argMatcher := mock.MatchedBy(func(r []*resource.Resource) bool { + if len(r) != 1 { + return false + } + return r[0].Name() == res.Name() + }) + repo := new(mockRepo) + repo.On("UpdateStatus", ctx, argMatcher).Return(nil) + logger := log.NewLogrus() + manager := service.NewResourceManager(repo, logger) + + storeService := new(mockDataStore) + storeService.On("Create", ctx, res).Return(errors.AlreadyExists(resource.EntityResource, "table already exists")) + storeService.On("Update", ctx, res).Return(errors.InternalError("resource", "error in update", nil)) + defer storeService.AssertExpectations(t) + + manager.RegisterDatastore(store, storeService) + + err = manager.SyncResource(ctx, res) + assert.NotNil(t, err) + assert.EqualError(t, err, "internal error for entity resource: unable to update on datastore: "+ + "internal error for entity resource: error in update") + }) + t.Run("returns error when fails to update in db", func(t *testing.T) { + spec := map[string]any{"description": "test spec"} + res, err := resource.NewResource("proj.ds.name1", "table", store, tnnt, meta, spec) + assert.Nil(t, err) + + argMatcher := mock.MatchedBy(func(r []*resource.Resource) bool { + if len(r) != 1 { + return false + } + return r[0].Name() == res.Name() + }) + repo := new(mockRepo) + repo.On("UpdateStatus", ctx, argMatcher).Return(errors.InternalError(resource.EntityResource, "error", nil)) + logger := log.NewLogrus() + manager := service.NewResourceManager(repo, logger) + + storeService := new(mockDataStore) + storeService.On("Create", ctx, res).Return(nil) + defer storeService.AssertExpectations(t) + + manager.RegisterDatastore(store, storeService) + + err = manager.SyncResource(ctx, res) + assert.NotNil(t, err) + assert.EqualError(t, err, "internal error for entity resource: unable to update status in database: "+ + "internal error for entity resource: error") + }) + t.Run("returns success when successful", func(t *testing.T) { + spec := map[string]any{"description": "test spec"} + res, err := resource.NewResource("proj.ds.name1", "table", store, tnnt, meta, spec) + assert.Nil(t, err) + + argMatcher := mock.MatchedBy(func(r []*resource.Resource) bool { + if len(r) != 1 { + return false + } + return r[0].Name() == res.Name() + }) + repo := new(mockRepo) + repo.On("UpdateStatus", ctx, argMatcher).Return(nil) + logger := log.NewLogrus() + manager := service.NewResourceManager(repo, logger) + + storeService := new(mockDataStore) + storeService.On("Create", ctx, res).Return(errors.AlreadyExists(resource.EntityResource, "table already exists")) + storeService.On("Update", ctx, res).Return(nil) + defer storeService.AssertExpectations(t) + + manager.RegisterDatastore(store, storeService) + + err = manager.SyncResource(ctx, res) + assert.Nil(t, err) + }) + }) } type mockRepo struct { diff --git a/core/resource/service/resource_service.go b/core/resource/service/resource_service.go index 23584329d9..89a17e3672 100644 --- a/core/resource/service/resource_service.go +++ b/core/resource/service/resource_service.go @@ -3,55 +3,79 @@ package service import ( "context" "fmt" - - "github.com/odpf/salt/log" - - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" + "reflect" + "strings" + + "github.com/raystack/salt/log" + + "github.com/raystack/optimus/core/event" + "github.com/raystack/optimus/core/event/moderator" + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/writer" ) type ResourceRepository interface { Create(ctx context.Context, res *resource.Resource) error Update(ctx context.Context, res *resource.Resource) error + ChangeNamespace(ctx context.Context, res *resource.Resource, newTenant tenant.Tenant) error ReadByFullName(ctx context.Context, tnnt tenant.Tenant, store resource.Store, fullName string) (*resource.Resource, error) ReadAll(ctx context.Context, tnnt tenant.Tenant, store resource.Store) ([]*resource.Resource, error) + GetResources(ctx context.Context, tnnt tenant.Tenant, store resource.Store, names []string) ([]*resource.Resource, error) } type ResourceManager interface { CreateResource(ctx context.Context, res *resource.Resource) error UpdateResource(ctx context.Context, res *resource.Resource) error + SyncResource(ctx context.Context, res *resource.Resource) error BatchUpdate(ctx context.Context, store resource.Store, resources []*resource.Resource) error Validate(res *resource.Resource) error GetURN(res *resource.Resource) (string, error) } +type DownstreamRefresher interface { + RefreshResourceDownstream(ctx context.Context, resourceURNs []job.ResourceURN, logWriter writer.LogWriter) error +} + type TenantDetailsGetter interface { GetDetails(ctx context.Context, tnnt tenant.Tenant) (*tenant.WithDetails, error) } +type EventHandler interface { + HandleEvent(moderator.Event) +} + type ResourceService struct { - repo ResourceRepository - mgr ResourceManager - tnntDetailsGetter TenantDetailsGetter + repo ResourceRepository + mgr ResourceManager + refresher DownstreamRefresher - logger log.Logger + logger log.Logger + eventHandler EventHandler } -func NewResourceService(logger log.Logger, repo ResourceRepository, mgr ResourceManager, tnntDetailsGetter TenantDetailsGetter) *ResourceService { +func NewResourceService( + logger log.Logger, + repo ResourceRepository, downstreamRefresher DownstreamRefresher, mgr ResourceManager, + eventHandler EventHandler, +) *ResourceService { return &ResourceService{ - repo: repo, - mgr: mgr, - tnntDetailsGetter: tnntDetailsGetter, - logger: logger, + repo: repo, + mgr: mgr, + refresher: downstreamRefresher, + logger: logger, + eventHandler: eventHandler, } } -func (rs ResourceService) Create(ctx context.Context, incoming *resource.Resource) error { +func (rs ResourceService) Create(ctx context.Context, incoming *resource.Resource) error { // nolint:gocritic if err := rs.mgr.Validate(incoming); err != nil { rs.logger.Error("error validating resource [%s]: %s", incoming.FullName(), err) return err } + incoming.MarkValidationSuccess() urn, err := rs.mgr.GetURN(incoming) if err != nil { @@ -69,11 +93,6 @@ func (rs ResourceService) Create(ctx context.Context, incoming *resource.Resourc rs.logger.Error("error getting resource [%s]: %s", incoming.FullName(), err) return err } - - if _, err := rs.tnntDetailsGetter.GetDetails(ctx, incoming.Tenant()); err != nil { - rs.logger.Error("error getting tenant for resource [%s] details: %s", incoming.FullName(), err) - return err - } incoming.MarkToCreate() if err := rs.repo.Create(ctx, incoming); err != nil { @@ -81,6 +100,9 @@ func (rs ResourceService) Create(ctx context.Context, incoming *resource.Resourc return err } } else { + if existing.Status() == resource.StatusSuccess || existing.Status() == resource.StatusExistInStore { + return nil // Note: return in case resource already exists + } if !resource.StatusForToCreate(existing.Status()) { msg := fmt.Sprintf("cannot create resource [%s] since it already exists with status [%s]", incoming.FullName(), existing.Status()) rs.logger.Error(msg) @@ -93,14 +115,22 @@ func (rs ResourceService) Create(ctx context.Context, incoming *resource.Resourc return err } } - return rs.mgr.CreateResource(ctx, incoming) + + if err := rs.mgr.CreateResource(ctx, incoming); err != nil { + rs.logger.Error("error creating resource [%s] to manager: %s", incoming.FullName(), err) + return err + } + + rs.raiseCreateEvent(incoming) + return nil } -func (rs ResourceService) Update(ctx context.Context, incoming *resource.Resource) error { +func (rs ResourceService) Update(ctx context.Context, incoming *resource.Resource, logWriter writer.LogWriter) error { // nolint:gocritic if err := rs.mgr.Validate(incoming); err != nil { rs.logger.Error("error validating resource [%s]: %s", incoming.FullName(), err) return err } + incoming.MarkValidationSuccess() urn, err := rs.mgr.GetURN(incoming) if err != nil { @@ -131,10 +161,41 @@ func (rs ResourceService) Update(ctx context.Context, incoming *resource.Resourc return err } - return rs.mgr.UpdateResource(ctx, incoming) + if err := rs.mgr.UpdateResource(ctx, incoming); err != nil { + rs.logger.Error("error updating resource [%s] to manager: %s", incoming.FullName(), err) + return err + } + + rs.raiseUpdateEvent(incoming) + + err = rs.handleRefreshDownstream(ctx, + []*resource.Resource{incoming}, + map[string]*resource.Resource{existing.FullName(): existing}, + logWriter, + ) + if err != nil { + rs.logger.Error("error refreshing downstream for resource [%s]: %s", incoming.FullName(), err) + return err + } + return nil } -func (rs ResourceService) Get(ctx context.Context, tnnt tenant.Tenant, store resource.Store, resourceFullName string) (*resource.Resource, error) { +func (rs ResourceService) ChangeNamespace(ctx context.Context, datastore resource.Store, resourceFullName string, oldTenant, newTenant tenant.Tenant) error { // nolint:gocritic + resourceSpec, err := rs.Get(ctx, oldTenant, datastore, resourceFullName) + if err != nil { + rs.logger.Error("failed to read existing resource [%s]: %s", resourceFullName, err) + return err + } + if err := rs.repo.ChangeNamespace(ctx, resourceSpec, newTenant); err != nil { + rs.logger.Error("error changing namespace of stored resource [%s]: %s", resourceSpec.FullName(), err) + return err + } + resourceSpec.UpdateTenant(newTenant) + rs.raiseUpdateEvent(resourceSpec) + return nil +} + +func (rs ResourceService) Get(ctx context.Context, tnnt tenant.Tenant, store resource.Store, resourceFullName string) (*resource.Resource, error) { // nolint:gocritic if resourceFullName == "" { rs.logger.Error("resource full name is empty") return nil, errors.InvalidArgument(resource.EntityResource, "empty resource full name") @@ -142,13 +203,43 @@ func (rs ResourceService) Get(ctx context.Context, tnnt tenant.Tenant, store res return rs.repo.ReadByFullName(ctx, tnnt, store, resourceFullName) } -func (rs ResourceService) GetAll(ctx context.Context, tnnt tenant.Tenant, store resource.Store) ([]*resource.Resource, error) { +func (rs ResourceService) GetAll(ctx context.Context, tnnt tenant.Tenant, store resource.Store) ([]*resource.Resource, error) { // nolint:gocritic return rs.repo.ReadAll(ctx, tnnt, store) } -func (rs ResourceService) Deploy(ctx context.Context, tnnt tenant.Tenant, store resource.Store, resources []*resource.Resource) error { - multiError := errors.NewMultiError("error batch updating resources") +func (rs ResourceService) SyncResources(ctx context.Context, tnnt tenant.Tenant, store resource.Store, names []string) (*resource.SyncResponse, error) { // nolint:gocritic + resources, err := rs.repo.GetResources(ctx, tnnt, store, names) + if err != nil { + rs.logger.Error("error getting resources [%s] from db: %s", strings.Join(names, ", "), err) + return nil, err + } + + synced := &resource.SyncResponse{ + IgnoredResources: findMissingResources(names, resources), + } + + if len(resources) == 0 { + return synced, nil + } + for _, r := range resources { + err := rs.mgr.SyncResource(ctx, r) + if err != nil { + synced.IgnoredResources = append(synced.IgnoredResources, resource.IgnoredResource{ + Name: r.Name().String(), + Reason: err.Error(), + }) + continue + } + synced.ResourceNames = append(synced.ResourceNames, r.FullName()) + } + + return synced, nil +} + +func (rs ResourceService) Deploy(ctx context.Context, tnnt tenant.Tenant, store resource.Store, incomings []*resource.Resource, logWriter writer.LogWriter) error { // nolint:gocritic + multiError := errors.NewMultiError("error batch updating resources") + for _, r := range incomings { if err := rs.mgr.Validate(r); err != nil { msg := fmt.Sprintf("error validating [%s]: %s", r.FullName(), err) multiError.Append(errors.Wrap(resource.EntityResource, msg, err)) @@ -173,27 +264,50 @@ func (rs ResourceService) Deploy(ctx context.Context, tnnt tenant.Tenant, store r.MarkValidationSuccess() } - toUpdateOnStore, err := rs.getResourcesToBatchUpdate(ctx, tnnt, store, resources) + existingResources, err := rs.repo.ReadAll(ctx, tnnt, store) + if err != nil { + rs.logger.Error("error reading all existing resources: %s", err) + multiError.Append(err) + return multiError.ToErr() + } + existingMappedByFullName := createFullNameToResourceMap(existingResources) + + toUpdateOnStore, err := rs.getResourcesToBatchUpdate(ctx, incomings, existingMappedByFullName) multiError.Append(err) if len(toUpdateOnStore) == 0 { rs.logger.Warn("no resources to be batch updated") - return errors.MultiToError(multiError) + return multiError.ToErr() + } + + var toCreate []*resource.Resource + var toUpdate []*resource.Resource + for _, r := range toUpdateOnStore { + if r.Status() == resource.StatusToCreate { + toCreate = append(toCreate, r) + } else if r.Status() == resource.StatusToUpdate { + toUpdate = append(toUpdate, r) + } } multiError.Append(rs.mgr.BatchUpdate(ctx, store, toUpdateOnStore)) - return errors.MultiToError(multiError) -} -func (rs ResourceService) getResourcesToBatchUpdate(ctx context.Context, tnnt tenant.Tenant, store resource.Store, incomings []*resource.Resource) ([]*resource.Resource, error) { - existingResources, readErr := rs.repo.ReadAll(ctx, tnnt, store) - if readErr != nil { - rs.logger.Error("error reading all existing resources: %s", readErr) - return nil, readErr + for _, r := range toCreate { + rs.raiseCreateEvent(r) } - existingMappedByFullName := createFullNameToResourceMap(existingResources) + for _, r := range toUpdate { + rs.raiseUpdateEvent(r) + } + + if err = rs.handleRefreshDownstream(ctx, toUpdate, existingMappedByFullName, logWriter); err != nil { + multiError.Append(err) + } + return multiError.ToErr() +} + +func (rs ResourceService) getResourcesToBatchUpdate(ctx context.Context, incomings []*resource.Resource, existingMappedByFullName map[string]*resource.Resource) ([]*resource.Resource, error) { // nolint:gocritic var toUpdateOnStore []*resource.Resource me := errors.NewMultiError("error in resources to batch update") @@ -231,7 +345,90 @@ func (rs ResourceService) getResourcesToBatchUpdate(ctx context.Context, tnnt te } me.Append(err) } - return toUpdateOnStore, errors.MultiToError(me) + return toUpdateOnStore, me.ToErr() +} + +func (rs ResourceService) raiseCreateEvent(res *resource.Resource) { // nolint:gocritic + if res.Status() != resource.StatusSuccess { + return + } + + ev, err := event.NewResourceCreatedEvent(res) + if err != nil { + rs.logger.Error("error creating event for resource create: %s", err) + return + } + rs.eventHandler.HandleEvent(ev) +} + +func (rs ResourceService) raiseUpdateEvent(res *resource.Resource) { // nolint:gocritic + if res.Status() != resource.StatusSuccess { + return + } + + ev, err := event.NewResourceUpdatedEvent(res) + if err != nil { + rs.logger.Error("error creating event for resource update: %s", err) + return + } + rs.eventHandler.HandleEvent(ev) +} + +func (rs ResourceService) handleRefreshDownstream( // nolint:gocritic + ctx context.Context, + incomings []*resource.Resource, + existingMappedByFullName map[string]*resource.Resource, + logWriter writer.LogWriter, +) error { + var resourceURNsToRefresh []job.ResourceURN + for _, incoming := range incomings { + if incoming.Status() != resource.StatusSuccess { + continue + } + + existing, ok := existingMappedByFullName[incoming.FullName()] + + skipMessage := fmt.Sprintf("downstream refresh for resource [%s] is skipped", existing.FullName()) + if !ok { + rs.logger.Warn(skipMessage) + logWriter.Write(writer.LogLevelWarning, skipMessage) + continue + } + + if rs.isToRefreshDownstream(incoming, existing) { + resourceURNsToRefresh = append(resourceURNsToRefresh, job.ResourceURN(incoming.URN())) + } else { + rs.logger.Warn(skipMessage) + logWriter.Write(writer.LogLevelWarning, skipMessage) + } + } + + if len(resourceURNsToRefresh) == 0 { + rs.logger.Info("no resource urns to which the refresh will be done upon") + return nil + } + + return rs.refresher.RefreshResourceDownstream(ctx, resourceURNsToRefresh, logWriter) +} + +func (ResourceService) isToRefreshDownstream(incoming, existing *resource.Resource) bool { + var key []string + for k := range incoming.Spec() { + key = append(key, k) + } + for k := range existing.Spec() { + key = append(key, k) + } + + // TODO: this is not ideal solution, we need to see how to get these 'special' fields + for _, k := range key { + switch strings.ToLower(k) { + case "view_query", "schema", "source": + return !reflect.DeepEqual(incoming.Spec()[k], existing.Spec()[k]) + } + } + + return false } func createFullNameToResourceMap(resources []*resource.Resource) map[string]*resource.Resource { diff --git a/core/resource/service/resource_service_test.go b/core/resource/service/resource_service_test.go index f1475dfdc4..ddcd83f78f 100644 --- a/core/resource/service/resource_service_test.go +++ b/core/resource/service/resource_service_test.go @@ -5,19 +5,23 @@ import ( "errors" "testing" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/resource/service" - "github.com/odpf/optimus/core/tenant" - oErrors "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/event/moderator" + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/resource/service" + "github.com/raystack/optimus/core/tenant" + oErrors "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/writer" ) func TestResourceService(t *testing.T) { ctx := context.Background() logger := log.NewLogrus() + logWriter := writer.NewLogWriter(logger) tnnt, tenantErr := tenant.NewTenant("project_test", "namespace_tes") assert.NoError(t, tenantErr) meta := &resource.Metadata{ @@ -36,7 +40,7 @@ func TestResourceService(t *testing.T) { mgr := newResourceManager(t) mgr.On("Validate", invalid).Return(errors.New("validation error")) - rscService := service.NewResourceService(logger, nil, mgr, nil) + rscService := service.NewResourceService(logger, nil, nil, mgr, nil) actualError := rscService.Create(ctx, invalid) assert.Error(t, actualError) @@ -50,7 +54,7 @@ func TestResourceService(t *testing.T) { mgr.On("Validate", incoming).Return(nil) mgr.On("GetURN", incoming).Return("", errors.New("urn error")) - rscService := service.NewResourceService(logger, nil, mgr, nil) + rscService := service.NewResourceService(logger, nil, nil, mgr, nil) actualError := rscService.Create(ctx, incoming) assert.Error(t, actualError) @@ -67,7 +71,7 @@ func TestResourceService(t *testing.T) { mgr.On("Validate", incoming).Return(nil) mgr.On("GetURN", incoming).Return(urn, nil) - rscService := service.NewResourceService(logger, nil, mgr, nil) + rscService := service.NewResourceService(logger, nil, nil, mgr, nil) actualError := rscService.Create(ctx, incoming) assert.Error(t, actualError) @@ -85,33 +89,13 @@ func TestResourceService(t *testing.T) { repo := newResourceRepository(t) repo.On("ReadByFullName", ctx, tnnt, resource.Bigquery, incoming.FullName()).Return(nil, errors.New("unknown error")) - rscService := service.NewResourceService(logger, repo, mgr, nil) + rscService := service.NewResourceService(logger, repo, nil, mgr, nil) actualError := rscService.Create(ctx, incoming) assert.ErrorContains(t, actualError, "unknown error") }) t.Run("resource does not exist in repository", func(t *testing.T) { - t.Run("returns error if error is encountered when getting tenant for the resource", func(t *testing.T) { - incoming, err := resource.NewResource("project.dataset", "dataset", resource.Bigquery, tnnt, meta, spec) - assert.NoError(t, err) - - mgr := newResourceManager(t) - mgr.On("Validate", incoming).Return(nil) - mgr.On("GetURN", incoming).Return("bigquery://project:dataset", nil) - - repo := newResourceRepository(t) - repo.On("ReadByFullName", ctx, tnnt, resource.Bigquery, incoming.FullName()).Return(nil, oErrors.NotFound(resource.EntityResource, "resource not found")) - - tnntDetailsGetter := newTenantDetailsGetter(t) - tnntDetailsGetter.On("GetDetails", ctx, tnnt).Return(nil, errors.New("error getting tenant")) - - rscService := service.NewResourceService(logger, repo, mgr, tnntDetailsGetter) - - actualError := rscService.Create(ctx, incoming) - assert.ErrorContains(t, actualError, "error getting tenant") - }) - t.Run("returns error if error is encountered when creating resource to repository", func(t *testing.T) { incoming, err := resource.NewResource("project.dataset", "dataset", resource.Bigquery, tnnt, meta, spec) assert.NoError(t, err) @@ -120,14 +104,11 @@ func TestResourceService(t *testing.T) { repo.On("ReadByFullName", ctx, tnnt, resource.Bigquery, incoming.FullName()).Return(nil, oErrors.NotFound(resource.EntityResource, "resource not found")) repo.On("Create", ctx, mock.Anything).Return(errors.New("error creating resource")) - tnntDetailsGetter := newTenantDetailsGetter(t) - tnntDetailsGetter.On("GetDetails", ctx, tnnt).Return(nil, nil) - mgr := newResourceManager(t) mgr.On("Validate", incoming).Return(nil) mgr.On("GetURN", incoming).Return("bigquery://project:dataset", nil) - rscService := service.NewResourceService(logger, repo, mgr, tnntDetailsGetter) + rscService := service.NewResourceService(logger, repo, nil, mgr, nil) actualError := rscService.Create(ctx, incoming) assert.ErrorContains(t, actualError, "error creating resource") @@ -135,7 +116,7 @@ func TestResourceService(t *testing.T) { }) t.Run("resource already exists in repository", func(t *testing.T) { - t.Run("returns error if status is neither create_failure nor to_create", func(t *testing.T) { + t.Run("returns no error if status is success or exists_in_store", func(t *testing.T) { existing, err := resource.NewResource("project.dataset", "dataset", resource.Bigquery, tnnt, meta, spec) assert.NoError(t, err) @@ -143,8 +124,34 @@ func TestResourceService(t *testing.T) { mgr.On("Validate", mock.Anything).Return(nil) mgr.On("GetURN", mock.Anything).Return("bigquery://project:dataset", nil) - repo := newResourceRepository(t) - rscService := service.NewResourceService(logger, repo, mgr, nil) + statusToTest := []resource.Status{ + resource.StatusExistInStore, + resource.StatusSuccess, + } + + for _, status := range statusToTest { + incoming, err := resource.NewResource("project.dataset", "dataset", resource.Bigquery, tnnt, meta, spec) + assert.NoError(t, err) + + existingWithStatus := resource.FromExisting(existing, resource.ReplaceStatus(status)) + + repo := newResourceRepository(t) + repo.On("ReadByFullName", ctx, tnnt, resource.Bigquery, incoming.FullName()).Return(existingWithStatus, nil) + + rscService := service.NewResourceService(logger, repo, nil, mgr, nil) + + err = rscService.Create(ctx, incoming) + assert.NoError(t, err) + repo.AssertExpectations(t) + } + }) + t.Run("returns error if status is neither create_failure nor to_create", func(t *testing.T) { + existing, err := resource.NewResource("project.dataset", "dataset", resource.Bigquery, tnnt, meta, spec) + assert.NoError(t, err) + + mgr := newResourceManager(t) + mgr.On("Validate", mock.Anything).Return(nil) + mgr.On("GetURN", mock.Anything).Return("bigquery://project:dataset", nil) unacceptableStatuses := []resource.Status{ resource.StatusUnknown, @@ -153,8 +160,6 @@ func TestResourceService(t *testing.T) { resource.StatusToUpdate, resource.StatusSkipped, resource.StatusUpdateFailure, - resource.StatusExistInStore, - resource.StatusSuccess, } for _, status := range unacceptableStatuses { @@ -163,6 +168,9 @@ func TestResourceService(t *testing.T) { existingWithStatus := resource.FromExisting(existing, resource.ReplaceStatus(status)) + repo := newResourceRepository(t) + rscService := service.NewResourceService(logger, repo, nil, mgr, nil) + repo.On("ReadByFullName", ctx, tnnt, resource.Bigquery, incoming.FullName()).Return(existingWithStatus, nil) actualError := rscService.Create(ctx, incoming) @@ -186,7 +194,7 @@ func TestResourceService(t *testing.T) { mgr.On("Validate", incoming).Return(nil) mgr.On("GetURN", incoming).Return("bigquery://project:dataset", nil) - rscService := service.NewResourceService(logger, repo, mgr, nil) + rscService := service.NewResourceService(logger, repo, nil, mgr, nil) actualError := rscService.Create(ctx, incoming) assert.ErrorContains(t, actualError, "error updating resource") @@ -206,10 +214,7 @@ func TestResourceService(t *testing.T) { mgr.On("GetURN", incoming).Return("bigquery://project:dataset", nil) mgr.On("CreateResource", ctx, incoming).Return(errors.New("error creating to store")) - tnntDetailsGetter := newTenantDetailsGetter(t) - tnntDetailsGetter.On("GetDetails", ctx, tnnt).Return(nil, nil) - - rscService := service.NewResourceService(logger, repo, mgr, tnntDetailsGetter) + rscService := service.NewResourceService(logger, repo, nil, mgr, nil) actualError := rscService.Create(ctx, incoming) assert.ErrorContains(t, actualError, "error creating to store") @@ -223,15 +228,14 @@ func TestResourceService(t *testing.T) { repo.On("ReadByFullName", ctx, tnnt, resource.Bigquery, incoming.FullName()).Return(nil, oErrors.NotFound(resource.EntityResource, "resource not found")) repo.On("Create", ctx, incoming).Return(nil) - tnntDetailsGetter := newTenantDetailsGetter(t) - tnntDetailsGetter.On("GetDetails", ctx, tnnt).Return(nil, nil) - mgr := newResourceManager(t) mgr.On("Validate", incoming).Return(nil) mgr.On("GetURN", incoming).Return("bigquery://project:dataset", nil) mgr.On("CreateResource", ctx, incoming).Return(nil) - rscService := service.NewResourceService(logger, repo, mgr, tnntDetailsGetter) + eventHandler := newEventHandler(t) + + rscService := service.NewResourceService(logger, repo, nil, mgr, eventHandler) actualError := rscService.Create(ctx, incoming) assert.NoError(t, actualError) @@ -245,9 +249,9 @@ func TestResourceService(t *testing.T) { mgr := newResourceManager(t) mgr.On("Validate", invalidResource).Return(errors.New("validation error")) - rscService := service.NewResourceService(logger, nil, mgr, nil) + rscService := service.NewResourceService(logger, nil, nil, mgr, nil) - actualError := rscService.Update(ctx, invalidResource) + actualError := rscService.Update(ctx, invalidResource, logWriter) assert.Error(t, actualError) }) t.Run("returns error cannot get resource urn", func(t *testing.T) { @@ -258,9 +262,9 @@ func TestResourceService(t *testing.T) { mgr.On("Validate", incoming).Return(nil) mgr.On("GetURN", incoming).Return("", errors.New("urn error")) - rscService := service.NewResourceService(logger, nil, mgr, nil) + rscService := service.NewResourceService(logger, nil, nil, mgr, nil) - actualError := rscService.Update(ctx, incoming) + actualError := rscService.Update(ctx, incoming, logWriter) assert.Error(t, actualError) assert.ErrorContains(t, actualError, "urn error") }) @@ -275,9 +279,9 @@ func TestResourceService(t *testing.T) { mgr.On("Validate", incoming).Return(nil) mgr.On("GetURN", incoming).Return(urn, nil) - rscService := service.NewResourceService(logger, nil, mgr, nil) + rscService := service.NewResourceService(logger, nil, nil, mgr, nil) - actualError := rscService.Update(ctx, incoming) + actualError := rscService.Update(ctx, incoming, logWriter) assert.Error(t, actualError) assert.ErrorContains(t, actualError, "urn already present") }) @@ -294,9 +298,9 @@ func TestResourceService(t *testing.T) { mgr.On("Validate", resourceToUpdate).Return(nil) mgr.On("GetURN", resourceToUpdate).Return("bigquery://project:dataset", nil) - rscService := service.NewResourceService(logger, repo, mgr, nil) + rscService := service.NewResourceService(logger, repo, nil, mgr, nil) - actualError := rscService.Update(ctx, resourceToUpdate) + actualError := rscService.Update(ctx, resourceToUpdate, logWriter) assert.ErrorContains(t, actualError, "unknown error") }) @@ -309,7 +313,7 @@ func TestResourceService(t *testing.T) { mgr.On("GetURN", mock.Anything).Return("bigquery://project:dataset", nil) repo := newResourceRepository(t) - rscService := service.NewResourceService(logger, repo, mgr, nil) + rscService := service.NewResourceService(logger, repo, nil, mgr, nil) unacceptableStatuses := []resource.Status{ resource.StatusUnknown, @@ -328,7 +332,7 @@ func TestResourceService(t *testing.T) { repo.On("ReadByFullName", ctx, tnnt, resource.Bigquery, resourceToUpdate.FullName()).Return(existingWithStatus, nil) - actualError := rscService.Update(ctx, resourceToUpdate) + actualError := rscService.Update(ctx, resourceToUpdate, logWriter) assert.ErrorContains(t, actualError, "cannot update resource") } }) @@ -349,9 +353,9 @@ func TestResourceService(t *testing.T) { repo.On("ReadByFullName", ctx, tnnt, resource.Bigquery, fullName).Return(existingResource, nil) repo.On("Update", ctx, mock.Anything).Return(errors.New("unknown error")) - rscService := service.NewResourceService(logger, repo, mgr, nil) + rscService := service.NewResourceService(logger, repo, nil, mgr, nil) - actualError := rscService.Update(ctx, resourceToUpdate) + actualError := rscService.Update(ctx, resourceToUpdate, logWriter) assert.ErrorContains(t, actualError, "unknown error") }) @@ -372,17 +376,55 @@ func TestResourceService(t *testing.T) { mgr.On("GetURN", mock.Anything).Return("bigquery://project:dataset", nil) mgr.On("UpdateResource", ctx, mock.Anything).Return(errors.New("unknown error")) - rscService := service.NewResourceService(logger, repo, mgr, nil) + rscService := service.NewResourceService(logger, repo, nil, mgr, nil) - actualError := rscService.Update(ctx, resourceToUpdate) + actualError := rscService.Update(ctx, resourceToUpdate, logWriter) + assert.ErrorContains(t, actualError, "unknown error") + }) + + t.Run("returns error if encountered error when refreshing downstream", func(t *testing.T) { + fullName := "project.dataset" + incomingSpec := map[string]any{"view_query": "select 1;"} + resourceToUpdate, err := resource.NewResource(fullName, "dataset", resource.Bigquery, tnnt, meta, incomingSpec) + assert.NoError(t, err) + existingSpec := map[string]any{"view_query": "select 2;"} + existingResource, err := resource.NewResource(fullName, "dataset", resource.Bigquery, tnnt, meta, existingSpec) + assert.NoError(t, err) + existingResource = resource.FromExisting(existingResource, resource.ReplaceStatus(resource.StatusToUpdate)) + + repo := newResourceRepository(t) + repo.On("ReadByFullName", ctx, tnnt, resource.Bigquery, fullName).Return(existingResource, nil) + repo.On("Update", ctx, mock.Anything).Return(nil) + + mgr := newResourceManager(t) + mgr.On("Validate", mock.Anything).Return(nil) + mgr.On("GetURN", mock.Anything).Return("bigquery://project:dataset", nil) + mgr.On("UpdateResource", ctx, mock.Anything).Run(func(args mock.Arguments) { + res, ok := args[1].(*resource.Resource) + if ok { + res.MarkSuccess() + } + }).Return(nil) + + eventHandler := newEventHandler(t) + eventHandler.On("HandleEvent", mock.Anything) + + refresher := new(mockDownstreamRefresher) + refresher.On("RefreshResourceDownstream", ctx, mock.Anything, logWriter).Return(errors.New("unknown error")) + + rscService := service.NewResourceService(logger, repo, refresher, mgr, eventHandler) + + actualError := rscService.Update(ctx, resourceToUpdate, logWriter) assert.ErrorContains(t, actualError, "unknown error") }) t.Run("returns nil if no error is encountered", func(t *testing.T) { fullName := "project.dataset" - resourceToUpdate, err := resource.NewResource(fullName, "dataset", resource.Bigquery, tnnt, meta, spec) + incomingSpec := map[string]any{"view_query": "select 1;"} + resourceToUpdate, err := resource.NewResource(fullName, "dataset", resource.Bigquery, tnnt, meta, incomingSpec) assert.NoError(t, err) - existingResource, err := resource.NewResource(fullName, "dataset", resource.Bigquery, tnnt, meta, spec) + existingSpec := map[string]any{"view_query": "select 2;"} + existingResource, err := resource.NewResource(fullName, "dataset", resource.Bigquery, tnnt, meta, existingSpec) assert.NoError(t, err) existingResource = resource.FromExisting(existingResource, resource.ReplaceStatus(resource.StatusToUpdate)) @@ -395,16 +437,21 @@ func TestResourceService(t *testing.T) { mgr.On("GetURN", mock.Anything).Return("bigquery://project:dataset", nil) mgr.On("UpdateResource", ctx, mock.Anything).Return(nil) - rscService := service.NewResourceService(logger, repo, mgr, nil) + eventHandler := newEventHandler(t) + + refresher := new(mockDownstreamRefresher) + refresher.On("RefreshResourceDownstream", ctx, mock.Anything, logWriter).Return(nil) + + rscService := service.NewResourceService(logger, repo, refresher, mgr, eventHandler) - actualError := rscService.Update(ctx, resourceToUpdate) + actualError := rscService.Update(ctx, resourceToUpdate, logWriter) assert.NoError(t, actualError) }) }) t.Run("Get", func(t *testing.T) { t.Run("returns nil and error if resource name is empty", func(t *testing.T) { - rscService := service.NewResourceService(logger, nil, nil, nil) + rscService := service.NewResourceService(logger, nil, nil, nil, nil) store := resource.Bigquery actualResource, actualError := rscService.Get(ctx, tnnt, store, "") @@ -417,7 +464,9 @@ func TestResourceService(t *testing.T) { fullName := "project.dataset" repo.On("ReadByFullName", ctx, tnnt, resource.Bigquery, fullName).Return(nil, errors.New("unknown error")) - rscService := service.NewResourceService(logger, repo, nil, nil) + refresher := new(mockDownstreamRefresher) + + rscService := service.NewResourceService(logger, repo, refresher, nil, nil) actualResource, actualError := rscService.Get(ctx, tnnt, resource.Bigquery, fullName) assert.Nil(t, actualResource) @@ -432,7 +481,9 @@ func TestResourceService(t *testing.T) { repo := newResourceRepository(t) repo.On("ReadByFullName", ctx, tnnt, resource.Bigquery, fullName).Return(existingResource, nil) - rscService := service.NewResourceService(logger, repo, nil, nil) + refresher := new(mockDownstreamRefresher) + + rscService := service.NewResourceService(logger, repo, refresher, nil, nil) actualResource, actualError := rscService.Get(ctx, tnnt, resource.Bigquery, fullName) assert.EqualValues(t, existingResource, actualResource) @@ -445,7 +496,9 @@ func TestResourceService(t *testing.T) { repo := newResourceRepository(t) repo.On("ReadAll", ctx, tnnt, resource.Bigquery).Return(nil, errors.New("unknown error")) - rscService := service.NewResourceService(logger, repo, nil, nil) + refresher := new(mockDownstreamRefresher) + + rscService := service.NewResourceService(logger, repo, refresher, nil, nil) actualResources, actualError := rscService.GetAll(ctx, tnnt, resource.Bigquery) assert.Nil(t, actualResources) @@ -459,7 +512,9 @@ func TestResourceService(t *testing.T) { repo := newResourceRepository(t) repo.On("ReadAll", ctx, tnnt, resource.Bigquery).Return([]*resource.Resource{existingResource}, nil) - rscService := service.NewResourceService(logger, repo, nil, nil) + refresher := new(mockDownstreamRefresher) + + rscService := service.NewResourceService(logger, repo, refresher, nil, nil) actualResources, actualError := rscService.GetAll(ctx, tnnt, resource.Bigquery) assert.EqualValues(t, []*resource.Resource{existingResource}, actualResources) @@ -471,8 +526,8 @@ func TestResourceService(t *testing.T) { viewSpec := map[string]any{ "view_query": "select * from `proj.dataset.table`", } - resourceWithStatus := func(name string, status resource.Status) *resource.Resource { - existingResource, resErr := resource.NewResource(name, "view", resource.Bigquery, tnnt, meta, viewSpec) + resourceWithStatus := func(name string, spec map[string]any, status resource.Status) *resource.Resource { + existingResource, resErr := resource.NewResource(name, "view", resource.Bigquery, tnnt, meta, spec) assert.NoError(t, resErr) return resource.FromExisting(existingResource, resource.ReplaceStatus(status)) } @@ -487,9 +542,9 @@ func TestResourceService(t *testing.T) { mgr := newResourceManager(t) mgr.On("Validate", invalidResourceToUpdate).Return(errors.New("error validating")) - rscService := service.NewResourceService(logger, repo, mgr, nil) + rscService := service.NewResourceService(logger, repo, nil, mgr, nil) - actualError := rscService.Deploy(ctx, tnnt, resource.Bigquery, resourcesToUpdate) + actualError := rscService.Deploy(ctx, tnnt, resource.Bigquery, resourcesToUpdate, logWriter) assert.Error(t, actualError) assert.ErrorContains(t, actualError, "error validating") }) @@ -505,9 +560,9 @@ func TestResourceService(t *testing.T) { repo := newResourceRepository(t) repo.On("ReadAll", ctx, tnnt, resource.Bigquery).Return([]*resource.Resource{}, nil) - rscService := service.NewResourceService(logger, repo, mgr, nil) + rscService := service.NewResourceService(logger, repo, nil, mgr, nil) - actualError := rscService.Deploy(ctx, tnnt, resource.Bigquery, []*resource.Resource{incoming}) + actualError := rscService.Deploy(ctx, tnnt, resource.Bigquery, []*resource.Resource{incoming}, logWriter) assert.Error(t, actualError) assert.ErrorContains(t, actualError, "urn error") assert.Equal(t, "unknown", incoming.Status().String()) @@ -527,16 +582,16 @@ func TestResourceService(t *testing.T) { repo := newResourceRepository(t) repo.On("ReadAll", ctx, tnnt, resource.Bigquery).Return([]*resource.Resource{}, nil) - rscService := service.NewResourceService(logger, repo, mgr, nil) + rscService := service.NewResourceService(logger, repo, nil, mgr, nil) - actualError := rscService.Deploy(ctx, tnnt, resource.Bigquery, []*resource.Resource{incoming}) + actualError := rscService.Deploy(ctx, tnnt, resource.Bigquery, []*resource.Resource{incoming}, logWriter) assert.Error(t, actualError) assert.ErrorContains(t, actualError, "urn already present for") assert.Equal(t, "unknown", incoming.Status().String()) }) t.Run("returns error if error is encountered when reading from repo", func(t *testing.T) { - incomingResourceToUpdate := resourceWithStatus("project.dataset.table1", resource.StatusValidationSuccess) + incomingResourceToUpdate := resourceWithStatus("project.dataset.table1", viewSpec, resource.StatusValidationSuccess) resourcesToUpdate := []*resource.Resource{incomingResourceToUpdate} repo := newResourceRepository(t) @@ -546,16 +601,16 @@ func TestResourceService(t *testing.T) { mgr.On("Validate", incomingResourceToUpdate).Return(nil) mgr.On("GetURN", incomingResourceToUpdate).Return("bigquery://project:dataset.table1", nil) - rscService := service.NewResourceService(logger, repo, mgr, nil) + rscService := service.NewResourceService(logger, repo, nil, mgr, nil) - actualError := rscService.Deploy(ctx, tnnt, resource.Bigquery, resourcesToUpdate) + actualError := rscService.Deploy(ctx, tnnt, resource.Bigquery, resourcesToUpdate, logWriter) assert.ErrorContains(t, actualError, "error while read all") }) t.Run("returns nil if there is no resource to create or modify", func(t *testing.T) { incomingResourceToUpdate, resErr := resource.NewResource("project.dataset.view1", "view", resource.Bigquery, tnnt, meta, viewSpec) assert.NoError(t, resErr) - existing := resourceWithStatus("project.dataset.view1", resource.StatusSuccess) + existing := resourceWithStatus("project.dataset.view1", viewSpec, resource.StatusSuccess) repo := newResourceRepository(t) repo.On("ReadAll", ctx, tnnt, resource.Bigquery).Return([]*resource.Resource{existing}, nil) @@ -564,9 +619,9 @@ func TestResourceService(t *testing.T) { mgr.On("Validate", mock.Anything).Return(nil) mgr.On("GetURN", mock.Anything).Return("bigquery://project:dataset.view1", nil) - rscService := service.NewResourceService(logger, repo, mgr, nil) + rscService := service.NewResourceService(logger, repo, nil, mgr, nil) - actualError := rscService.Deploy(ctx, tnnt, resource.Bigquery, []*resource.Resource{incomingResourceToUpdate}) + actualError := rscService.Deploy(ctx, tnnt, resource.Bigquery, []*resource.Resource{incomingResourceToUpdate}, logWriter) assert.NoError(t, actualError) }) @@ -586,9 +641,11 @@ func TestResourceService(t *testing.T) { mgr.On("Validate", mock.Anything).Return(nil) mgr.On("GetURN", mock.Anything).Return("bigquery://project:dataset", nil) - rscService := service.NewResourceService(logger, repo, mgr, nil) + eventHandler := newEventHandler(t) + + rscService := service.NewResourceService(logger, repo, nil, mgr, eventHandler) - actualError := rscService.Deploy(ctx, tnnt, resource.Bigquery, []*resource.Resource{incomingResourceToUpdate}) + actualError := rscService.Deploy(ctx, tnnt, resource.Bigquery, []*resource.Resource{incomingResourceToUpdate}, logWriter) assert.ErrorContains(t, actualError, "error in create") }) @@ -601,7 +658,7 @@ func TestResourceService(t *testing.T) { incomingResourceToUpdate, err := resource.NewResource(fullName, "view", resource.Bigquery, tnnt, incomingMetadata, viewSpec) assert.NoError(t, err) - existing := resourceWithStatus(fullName, resource.StatusSuccess) + existing := resourceWithStatus(fullName, viewSpec, resource.StatusSuccess) repo := newResourceRepository(t) repo.On("ReadAll", ctx, tnnt, resource.Bigquery).Return([]*resource.Resource{existing}, nil) @@ -611,9 +668,10 @@ func TestResourceService(t *testing.T) { mgr.On("Validate", mock.Anything).Return(nil) mgr.On("GetURN", mock.Anything).Return("bigquery://project:dataset.view1", nil) - rscService := service.NewResourceService(logger, repo, mgr, nil) + eventHandler := newEventHandler(t) + rscService := service.NewResourceService(logger, repo, nil, mgr, eventHandler) - actualError := rscService.Deploy(ctx, tnnt, resource.Bigquery, []*resource.Resource{incomingResourceToUpdate}) + actualError := rscService.Deploy(ctx, tnnt, resource.Bigquery, []*resource.Resource{incomingResourceToUpdate}, logWriter) assert.ErrorContains(t, actualError, "error in update") }) @@ -626,7 +684,7 @@ func TestResourceService(t *testing.T) { incomingResourceToUpdate, err := resource.NewResource(fullName, "view", resource.Bigquery, tnnt, incomingMetadata, viewSpec) assert.NoError(t, err) - existing := resourceWithStatus(fullName, resource.StatusCreateFailure) + existing := resourceWithStatus(fullName, viewSpec, resource.StatusCreateFailure) repo := newResourceRepository(t) repo.On("ReadAll", ctx, tnnt, resource.Bigquery).Return([]*resource.Resource{existing}, nil) @@ -637,9 +695,10 @@ func TestResourceService(t *testing.T) { mgr.On("GetURN", mock.Anything).Return("bigquery://project:dataset.view1", nil) mgr.On("BatchUpdate", ctx, resource.Bigquery, mock.Anything).Return(errors.New("unknown error")) - rscService := service.NewResourceService(logger, repo, mgr, nil) + eventHandler := newEventHandler(t) + rscService := service.NewResourceService(logger, repo, nil, mgr, eventHandler) - actualError := rscService.Deploy(ctx, tnnt, resource.Bigquery, []*resource.Resource{incomingResourceToUpdate}) + actualError := rscService.Deploy(ctx, tnnt, resource.Bigquery, []*resource.Resource{incomingResourceToUpdate}, logWriter) assert.ErrorContains(t, actualError, "unknown error") }) @@ -665,21 +724,71 @@ func TestResourceService(t *testing.T) { mgr.On("GetURN", mock.Anything).Return("bigquery://project:dataset.view1", nil) mgr.On("BatchUpdate", ctx, resource.Bigquery, mock.Anything).Return(errors.New("unknown error")) - rscService := service.NewResourceService(logger, repo, mgr, nil) + eventHandler := newEventHandler(t) + rscService := service.NewResourceService(logger, repo, nil, mgr, eventHandler) - actualError := rscService.Deploy(ctx, tnnt, resource.Bigquery, []*resource.Resource{incomingResourceToUpdate}) + actualError := rscService.Deploy(ctx, tnnt, resource.Bigquery, []*resource.Resource{incomingResourceToUpdate}, logWriter) + assert.ErrorContains(t, actualError, "unknown error") + }) + + t.Run("returns nil if encountered error when refreshing downstream", func(t *testing.T) { + existingToCreate := resourceWithStatus("project.dataset.view1", viewSpec, resource.StatusCreateFailure) + existingToSkip := resourceWithStatus("project.dataset.view2", viewSpec, resource.StatusSuccess) + existingToUpdate := resourceWithStatus("project.dataset.view3", viewSpec, resource.StatusUpdateFailure) + + updatedViewSpec := map[string]any{ + "view_query": "select 1;", + } + incomingToUpdate, err := resource.NewResource("project.dataset.view3", "view", resource.Bigquery, tnnt, meta, updatedViewSpec) + assert.NoError(t, err) + incomingToCreateExisting, resErr := resource.NewResource("project.dataset.view1", "view", resource.Bigquery, tnnt, meta, viewSpec) + assert.NoError(t, resErr) + incomingToSkip, resErr := resource.NewResource("project.dataset.view2", "view", resource.Bigquery, tnnt, meta, viewSpec) + assert.NoError(t, resErr) + incomingToCreate, resErr := resource.NewResource("project.dataset.view5", "view", resource.Bigquery, tnnt, meta, viewSpec) + assert.NoError(t, resErr) + + repo := newResourceRepository(t) + repo.On("ReadAll", ctx, tnnt, resource.Bigquery).Return([]*resource.Resource{existingToCreate, existingToSkip, existingToUpdate}, nil) + repo.On("Create", ctx, incomingToCreate).Return(nil) + repo.On("Update", ctx, incomingToUpdate).Return(nil) + repo.On("Update", ctx, incomingToCreateExisting).Return(nil) + + mgr := newResourceManager(t) + mgr.On("Validate", mock.Anything).Return(nil) + mgr.On("GetURN", mock.Anything).Return("bigquery://project:dataset.view1", nil) + mgr.On("BatchUpdate", ctx, resource.Bigquery, []*resource.Resource{incomingToCreate, incomingToUpdate, incomingToCreateExisting}).Run(func(args mock.Arguments) { + res := args.Get(2).([]*resource.Resource) + for _, r := range res { + r.MarkSuccess() + } + }).Return(nil) + + eventHandler := newEventHandler(t) + argMatcher := mock.MatchedBy(func(ev moderator.Event) bool { + return ev != nil + }) + eventHandler.On("HandleEvent", argMatcher).Return().Times(3) + + refresher := new(mockDownstreamRefresher) + refresher.On("RefreshResourceDownstream", ctx, mock.Anything, logWriter).Return(errors.New("unknown error")) + + rscService := service.NewResourceService(logger, repo, refresher, mgr, eventHandler) + + incomings := []*resource.Resource{incomingToCreate, incomingToSkip, incomingToUpdate, incomingToCreateExisting} + actualError := rscService.Deploy(ctx, tnnt, resource.Bigquery, incomings, logWriter) assert.ErrorContains(t, actualError, "unknown error") }) t.Run("returns nil if no error is encountered", func(t *testing.T) { - existingToCreate := resourceWithStatus("project.dataset.view1", resource.StatusCreateFailure) - existingToSkip := resourceWithStatus("project.dataset.view2", resource.StatusSuccess) - existingToUpdate := resourceWithStatus("project.dataset.view3", resource.StatusUpdateFailure) + existingToCreate := resourceWithStatus("project.dataset.view1", viewSpec, resource.StatusCreateFailure) + existingToSkip := resourceWithStatus("project.dataset.view2", viewSpec, resource.StatusSuccess) + existingToUpdate := resourceWithStatus("project.dataset.view3", viewSpec, resource.StatusUpdateFailure) - incomingMetadata := &resource.Metadata{ - Description: "incoming resource metadata", + updatedViewSpec := map[string]any{ + "view_query": "select 1;", } - incomingToUpdate, err := resource.NewResource("project.dataset.view3", "view", resource.Bigquery, tnnt, incomingMetadata, viewSpec) + incomingToUpdate, err := resource.NewResource("project.dataset.view3", "view", resource.Bigquery, tnnt, meta, updatedViewSpec) assert.NoError(t, err) incomingToCreateExisting, resErr := resource.NewResource("project.dataset.view1", "view", resource.Bigquery, tnnt, meta, viewSpec) assert.NoError(t, resErr) @@ -697,15 +806,99 @@ func TestResourceService(t *testing.T) { mgr := newResourceManager(t) mgr.On("Validate", mock.Anything).Return(nil) mgr.On("GetURN", mock.Anything).Return("bigquery://project:dataset.view1", nil) - mgr.On("BatchUpdate", ctx, resource.Bigquery, []*resource.Resource{incomingToCreate, incomingToUpdate, incomingToCreateExisting}).Return(nil) + mgr.On("BatchUpdate", ctx, resource.Bigquery, []*resource.Resource{incomingToCreate, incomingToUpdate, incomingToCreateExisting}).Run(func(args mock.Arguments) { + res := args.Get(2).([]*resource.Resource) + for _, r := range res { + r.MarkSuccess() + } + }).Return(nil) + + eventHandler := newEventHandler(t) + argMatcher := mock.MatchedBy(func(ev moderator.Event) bool { + return ev != nil + }) + eventHandler.On("HandleEvent", argMatcher).Return().Times(3) + + refresher := new(mockDownstreamRefresher) + refresher.On("RefreshResourceDownstream", ctx, mock.Anything, logWriter).Return(nil) - rscService := service.NewResourceService(logger, repo, mgr, nil) + rscService := service.NewResourceService(logger, repo, refresher, mgr, eventHandler) incomings := []*resource.Resource{incomingToCreate, incomingToSkip, incomingToUpdate, incomingToCreateExisting} - actualError := rscService.Deploy(ctx, tnnt, resource.Bigquery, incomings) + actualError := rscService.Deploy(ctx, tnnt, resource.Bigquery, incomings, logWriter) assert.NoError(t, actualError) }) }) + + t.Run("SyncResource", func(t *testing.T) { + t.Run("returns error when get resources returns error", func(t *testing.T) { + fullName := "project.dataset" + repo := newResourceRepository(t) + repo.On("GetResources", ctx, tnnt, resource.Bigquery, []string{fullName}).Return(nil, errors.New("unknown error")) + + mgr := newResourceManager(t) + + rscService := service.NewResourceService(logger, repo, nil, mgr, nil) + + resp, actualError := rscService.SyncResources(ctx, tnnt, resource.Bigquery, []string{fullName}) + assert.ErrorContains(t, actualError, "unknown error") + assert.Nil(t, resp) + }) + t.Run("returns if no resources to sync", func(t *testing.T) { + fullName := "project.dataset" + repo := newResourceRepository(t) + repo.On("GetResources", ctx, tnnt, resource.Bigquery, []string{fullName}).Return([]*resource.Resource{}, nil) + + mgr := newResourceManager(t) + + rscService := service.NewResourceService(logger, repo, nil, mgr, nil) + + response, actualError := rscService.SyncResources(ctx, tnnt, resource.Bigquery, []string{fullName}) + assert.Nil(t, actualError) + assert.Equal(t, fullName, response.IgnoredResources[0].Name) + assert.Equal(t, "no resource found in namespace", response.IgnoredResources[0].Reason) + assert.Equal(t, 0, len(response.ResourceNames)) + }) + t.Run("returns error while syncing resource", func(t *testing.T) { + fullName := "project.dataset" + incoming, err := resource.NewResource(fullName, "dataset", resource.Bigquery, tnnt, meta, spec) + assert.NoError(t, err) + + repo := newResourceRepository(t) + repo.On("GetResources", ctx, tnnt, resource.Bigquery, []string{fullName}). + Return([]*resource.Resource{incoming}, nil) + + mgr := newResourceManager(t) + mgr.On("SyncResource", ctx, incoming).Return(errors.New("unable to create")) + + rscService := service.NewResourceService(logger, repo, nil, mgr, nil) + + response, actualError := rscService.SyncResources(ctx, tnnt, resource.Bigquery, []string{fullName}) + assert.Nil(t, actualError) + assert.Equal(t, fullName, response.IgnoredResources[0].Name) + assert.Equal(t, "unable to create", response.IgnoredResources[0].Reason) + assert.Equal(t, 0, len(response.ResourceNames)) + }) + t.Run("syncs the resource successfully", func(t *testing.T) { + fullName := "project.dataset" + incoming, err := resource.NewResource(fullName, "dataset", resource.Bigquery, tnnt, meta, spec) + assert.NoError(t, err) + + repo := newResourceRepository(t) + repo.On("GetResources", ctx, tnnt, resource.Bigquery, []string{fullName}). + Return([]*resource.Resource{incoming}, nil) + + mgr := newResourceManager(t) + mgr.On("SyncResource", ctx, incoming).Return(nil) + + rscService := service.NewResourceService(logger, repo, nil, mgr, nil) + + response, actualError := rscService.SyncResources(ctx, tnnt, resource.Bigquery, []string{fullName}) + assert.Nil(t, actualError) + assert.Equal(t, fullName, response.ResourceNames[0]) + assert.Equal(t, 0, len(response.IgnoredResources)) + }) + }) } type mockResourceRepository struct { @@ -736,6 +929,18 @@ func (m *mockResourceRepository) Update(ctx context.Context, res *resource.Resou return m.Called(ctx, res).Error(0) } +func (m *mockResourceRepository) ChangeNamespace(ctx context.Context, res *resource.Resource, newTenant tenant.Tenant) error { + return m.Called(ctx, res, newTenant).Error(0) +} + +func (m *mockResourceRepository) GetResources(ctx context.Context, tnnt tenant.Tenant, store resource.Store, names []string) ([]*resource.Resource, error) { + args := m.Called(ctx, tnnt, store, names) + if args.Get(0) == nil { + return nil, args.Error(1) + } + return args.Get(0).([]*resource.Resource), args.Error(1) +} + type mockConstructorTestingTNewResourceRepository interface { mock.TestingT Cleanup(func()) @@ -775,6 +980,10 @@ func (m *mockResourceManager) GetURN(res *resource.Resource) (string, error) { return args.Get(0).(string), args.Error(1) } +func (m *mockResourceManager) SyncResource(ctx context.Context, res *resource.Resource) error { + return m.Called(ctx, res).Error(0) +} + type mockConstructorTestingTNewResourceManager interface { mock.TestingT Cleanup(func()) @@ -789,28 +998,32 @@ func newResourceManager(t mockConstructorTestingTNewResourceManager) *mockResour return mock } -type mockTenantDetailsGetter struct { +type mockEventHandler struct { mock.Mock } -func (m *mockTenantDetailsGetter) GetDetails(ctx context.Context, tnnt tenant.Tenant) (*tenant.WithDetails, error) { - args := m.Called(ctx, tnnt) - if args.Get(0) == nil { - return nil, args.Error(1) - } - return args.Get(0).(*tenant.WithDetails), args.Error(1) +func (m *mockEventHandler) HandleEvent(e moderator.Event) { + m.Called(e) } -type mockConstructorTestingTNewTenantDetailsGetter interface { +type mockConstructorEventHandler interface { mock.TestingT Cleanup(func()) } -func newTenantDetailsGetter(t mockConstructorTestingTNewTenantDetailsGetter) *mockTenantDetailsGetter { - mock := &mockTenantDetailsGetter{} +func newEventHandler(t mockConstructorEventHandler) *mockEventHandler { + mock := &mockEventHandler{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) return mock } + +type mockDownstreamRefresher struct { + mock.Mock +} + +func (m *mockDownstreamRefresher) RefreshResourceDownstream(ctx context.Context, resourceURNs []job.ResourceURN, logWriter writer.LogWriter) error { + return m.Called(ctx, resourceURNs, logWriter).Error(0) +} diff --git a/core/resource/status.go b/core/resource/status.go index e3f2ab9462..f9d5927ef2 100644 --- a/core/resource/status.go +++ b/core/resource/status.go @@ -60,3 +60,8 @@ func StatusForToUpdate(status Status) bool { func StatusIsSuccess(status Status) bool { return status == StatusSuccess } + +type SyncResponse struct { + ResourceNames []string + IgnoredResources []IgnoredResource +} diff --git a/core/resource/status_test.go b/core/resource/status_test.go index 4f84ff749c..2e7a183657 100644 --- a/core/resource/status_test.go +++ b/core/resource/status_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/resource" + "github.com/raystack/optimus/core/resource" ) func TestStatus(t *testing.T) { diff --git a/core/resource/store.go b/core/resource/store.go index 506e3cc4d9..20daee9973 100644 --- a/core/resource/store.go +++ b/core/resource/store.go @@ -1,6 +1,6 @@ package resource -import "github.com/odpf/optimus/internal/errors" +import "github.com/raystack/optimus/internal/errors" const ( Bigquery Store = "bigquery" diff --git a/core/resource/store_test.go b/core/resource/store_test.go index 7b9cdbd19c..3ea5258f45 100644 --- a/core/resource/store_test.go +++ b/core/resource/store_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/resource" + "github.com/raystack/optimus/core/resource" ) func TestDataStore(t *testing.T) { diff --git a/core/scheduler/event.go b/core/scheduler/event.go index 6de531ccaa..55f32ae4e0 100644 --- a/core/scheduler/event.go +++ b/core/scheduler/event.go @@ -7,9 +7,9 @@ import ( "github.com/mitchellh/mapstructure" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" - "github.com/odpf/optimus/internal/utils" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/utils" ) type ( diff --git a/core/scheduler/event_test.go b/core/scheduler/event_test.go index 6896566aa6..955f3319a8 100644 --- a/core/scheduler/event_test.go +++ b/core/scheduler/event_test.go @@ -6,8 +6,8 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/tenant" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/tenant" ) func TestFromStringToEventType(t *testing.T) { diff --git a/core/scheduler/executor.go b/core/scheduler/executor.go index bfc6f95047..3a3b8df69d 100644 --- a/core/scheduler/executor.go +++ b/core/scheduler/executor.go @@ -4,8 +4,8 @@ import ( "strings" "time" - "github.com/odpf/optimus/internal/errors" - "github.com/odpf/optimus/internal/utils" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/utils" ) const ( diff --git a/core/scheduler/executor_test.go b/core/scheduler/executor_test.go index c0c78678a5..edb54dd43c 100644 --- a/core/scheduler/executor_test.go +++ b/core/scheduler/executor_test.go @@ -7,7 +7,7 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/scheduler" + "github.com/raystack/optimus/core/scheduler" ) func TestExecutor(t *testing.T) { diff --git a/core/scheduler/handler/v1beta1/job_run.go b/core/scheduler/handler/v1beta1/job_run.go index 4ad3afbcc8..c72a7ff7a2 100644 --- a/core/scheduler/handler/v1beta1/job_run.go +++ b/core/scheduler/handler/v1beta1/job_run.go @@ -2,15 +2,14 @@ package v1beta1 import ( "context" - "encoding/json" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "google.golang.org/protobuf/types/known/timestamppb" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) type JobRunService interface { @@ -35,31 +34,37 @@ type JobRunHandler struct { func (h JobRunHandler) JobRunInput(ctx context.Context, req *pb.JobRunInputRequest) (*pb.JobRunInputResponse, error) { projectName, err := tenant.ProjectNameFrom(req.GetProjectName()) if err != nil { + h.l.Error("error adapting project name [%s]: %s", req.GetProjectName(), err) return nil, errors.GRPCErr(err, "unable to get job run input for "+req.GetJobName()) } jobName, err := scheduler.JobNameFrom(req.GetJobName()) if err != nil { + h.l.Error("error adapting job name [%s]: %s", req.GetJobName(), err) return nil, errors.GRPCErr(err, "unable to get job run input for "+req.GetJobName()) } executor, err := scheduler.ExecutorFromEnum(req.InstanceName, req.InstanceType.String()) if err != nil { + h.l.Error("error adapting executor: %s", err) return nil, errors.GRPCErr(err, "unable to get job run input for "+req.GetJobName()) } err = req.ScheduledAt.CheckValid() if err != nil { + h.l.Error("invalid scheduled at: %s", err) return nil, errors.GRPCErr(errors.InvalidArgument(scheduler.EntityJobRun, "invalid scheduled_at"), "unable to get job run input for "+req.GetJobName()) } runConfig, err := scheduler.RunConfigFrom(executor, req.ScheduledAt.AsTime(), req.JobrunId) if err != nil { + h.l.Error("error adapting run config: %s", err) return nil, errors.GRPCErr(err, "unable to get job run input for "+req.GetJobName()) } input, err := h.service.JobRunInput(ctx, projectName, jobName, runConfig) if err != nil { + h.l.Error("error getting job run input: %s", err) return nil, errors.GRPCErr(err, "unable to get job run input for "+req.GetJobName()) } @@ -75,22 +80,26 @@ func (h JobRunHandler) JobRunInput(ctx context.Context, req *pb.JobRunInputReque func (h JobRunHandler) JobRun(ctx context.Context, req *pb.JobRunRequest) (*pb.JobRunResponse, error) { projectName, err := tenant.ProjectNameFrom(req.GetProjectName()) if err != nil { + h.l.Error("error adapting project name [%s]: %s", req.GetProjectName(), err) return nil, errors.GRPCErr(err, "unable to get job run for "+req.GetJobName()) } jobName, err := scheduler.JobNameFrom(req.GetJobName()) if err != nil { + h.l.Error("error adapting job name [%s]: %s", req.GetJobName(), err) return nil, errors.GRPCErr(err, "unable to get job run for "+req.GetJobName()) } criteria, err := buildCriteriaForJobRun(req) if err != nil { + h.l.Error("error building job run criteria: %s", err) return nil, errors.GRPCErr(err, "unable to get job run for "+req.GetJobName()) } var jobRuns []*scheduler.JobRunStatus jobRuns, err = h.service.GetJobRuns(ctx, projectName, jobName, criteria) if err != nil { + h.l.Error("error getting job runs: %s", err) return nil, errors.GRPCErr(err, "unable to get job run for "+req.GetJobName()) } @@ -126,15 +135,18 @@ func buildCriteriaForJobRun(req *pb.JobRunRequest) (*scheduler.JobRunsCriteria, }, nil } -func (h JobRunHandler) UploadToScheduler(ctx context.Context, req *pb.UploadToSchedulerRequest) (*pb.UploadToSchedulerResponse, error) { +func (h JobRunHandler) UploadToScheduler(_ context.Context, req *pb.UploadToSchedulerRequest) (*pb.UploadToSchedulerResponse, error) { projectName, err := tenant.ProjectNameFrom(req.GetProjectName()) if err != nil { + h.l.Error("error adapting project name [%s]: %s", req.GetProjectName(), err) return nil, errors.GRPCErr(err, "unable to get projectName") } - err = h.service.UploadToScheduler(ctx, projectName) - if err != nil { - return nil, errors.GRPCErr(err, "\nuploaded to scheduler with error") - } + go func() { + err = h.service.UploadToScheduler(context.Background(), projectName) + if err != nil { + h.l.Error("Finished upload to scheduler with error: %s", err) + } + }() return &pb.UploadToSchedulerResponse{}, nil } @@ -142,33 +154,33 @@ func (h JobRunHandler) UploadToScheduler(ctx context.Context, req *pb.UploadToSc func (h JobRunHandler) RegisterJobEvent(ctx context.Context, req *pb.RegisterJobEventRequest) (*pb.RegisterJobEventResponse, error) { tnnt, err := tenant.NewTenant(req.GetProjectName(), req.GetNamespaceName()) if err != nil { + h.l.Error("invalid tenant information request project [%s] namespace [%s]: %s", req.GetProjectName(), req.GetNamespaceName(), err) return nil, errors.GRPCErr(err, "unable to get tenant") } jobName, err := scheduler.JobNameFrom(req.GetJobName()) if err != nil { + h.l.Error("error adapting job name [%s]: %s", jobName, err) return nil, errors.GRPCErr(err, "unable to get job name"+req.GetJobName()) } event, err := scheduler.EventFrom(req.GetEvent().Type.String(), req.GetEvent().Value.AsMap(), jobName, tnnt) if err != nil { + h.l.Error("error adapting event: %s", err) return nil, errors.GRPCErr(err, "unable to parse event") } - multiError := errors.NewMultiError("errors in RegisterJobEvent") + me := errors.NewMultiError("errors in RegisterJobEvent") err = h.service.UpdateJobState(ctx, event) if err != nil { - jobEventByteString, _ := json.Marshal(req.GetEvent()) - h.l.Error(errors.InternalError(scheduler.EntityJobRun, "scheduler could not update job run state, event Payload::"+string(jobEventByteString), err).Error()) - multiError.Append(errors.InternalError(scheduler.EntityJobRun, "scheduler could not update job run state, err:"+err.Error(), err)) + h.l.Error("error updating job run state for Job: %s, Project: %s, eventType: %s, schedule_at: %s, err: %s", jobName, tnnt.ProjectName(), event.Type, event.JobScheduledAt.String(), err.Error()) + me.Append(errors.AddErrContext(err, scheduler.EntityJobRun, "scheduler could not update job run state")) } err = h.notifier.Push(ctx, event) - if err != nil { - multiError.Append(err) - } + me.Append(err) - return &pb.RegisterJobEventResponse{}, errors.MultiToError(multiError) + return &pb.RegisterJobEventResponse{}, me.ToErr() } func NewJobRunHandler(l log.Logger, service JobRunService, notifier Notifier) *JobRunHandler { diff --git a/core/scheduler/handler/v1beta1/job_run_test.go b/core/scheduler/handler/v1beta1/job_run_test.go index 7d49320a04..0142c5cd92 100644 --- a/core/scheduler/handler/v1beta1/job_run_test.go +++ b/core/scheduler/handler/v1beta1/job_run_test.go @@ -6,16 +6,16 @@ import ( "testing" "time" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/scheduler/handler/v1beta1" - "github.com/odpf/optimus/core/tenant" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/scheduler/handler/v1beta1" + "github.com/raystack/optimus/core/tenant" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) const ( @@ -356,24 +356,7 @@ func TestJobRunHandler(t *testing.T) { assert.EqualError(t, err, "rpc error: code = InvalidArgument desc = invalid argument for entity project: project name is empty: unable to get projectName") assert.Nil(t, resp) }) - t.Run("should fail deployment if UploadToScheduler service fails", func(t *testing.T) { - namespaceName := "namespace-name" - req := &pb.UploadToSchedulerRequest{ - ProjectName: projectName, - NamespaceName: &namespaceName, - } - jobRunService := new(mockJobRunService) - jobRunService.On("UploadToScheduler", ctx, tenant.ProjectName(projectName)). - Return(fmt.Errorf("some error")) - defer jobRunService.AssertExpectations(t) - jobRunHandler := v1beta1.NewJobRunHandler(logger, jobRunService, nil) - - resp, err := jobRunHandler.UploadToScheduler(ctx, req) - assert.NotNil(t, err) - assert.EqualError(t, err, "rpc error: code = Internal desc = some error: \nuploaded to scheduler with error") - assert.Nil(t, resp) - }) - t.Run("should return success if deployment succeeds", func(t *testing.T) { + t.Run("should return after triggering deploy to scheduler", func(t *testing.T) { namespaceName := "namespace-name" req := &pb.UploadToSchedulerRequest{ ProjectName: projectName, @@ -381,7 +364,6 @@ func TestJobRunHandler(t *testing.T) { } jobRunService := new(mockJobRunService) jobRunService.On("UploadToScheduler", ctx, tenant.ProjectName(projectName)).Return(nil) - defer jobRunService.AssertExpectations(t) jobRunHandler := v1beta1.NewJobRunHandler(logger, jobRunService, nil) _, err := jobRunHandler.UploadToScheduler(ctx, req) @@ -522,7 +504,7 @@ func TestJobRunHandler(t *testing.T) { resp, err := jobRunHandler.RegisterJobEvent(ctx, req) assert.NotNil(t, err) - assert.EqualError(t, err, "errors in RegisterJobEvent:\n internal error for entity jobRun: scheduler could not update job run state, err:some error") + assert.ErrorContains(t, err, "scheduler could not update job run state") assert.Equal(t, &pb.RegisterJobEventResponse{}, resp) }) t.Run("should return error if notify Push fails", func(t *testing.T) { diff --git a/core/scheduler/handler/v1beta1/replay.go b/core/scheduler/handler/v1beta1/replay.go index cca0da6ffa..2aa675b5dd 100644 --- a/core/scheduler/handler/v1beta1/replay.go +++ b/core/scheduler/handler/v1beta1/replay.go @@ -1,21 +1,23 @@ package v1beta1 import ( - "fmt" "strings" "github.com/google/uuid" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "golang.org/x/net/context" + "google.golang.org/protobuf/types/known/timestamppb" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) type ReplayService interface { CreateReplay(ctx context.Context, tenant tenant.Tenant, jobName scheduler.JobName, config *scheduler.ReplayConfig) (replayID uuid.UUID, err error) + GetReplayList(ctx context.Context, projectName tenant.ProjectName) (replays []*scheduler.Replay, err error) + GetReplayByID(ctx context.Context, replayID uuid.UUID) (replay *scheduler.ReplayWithRun, err error) } type ReplayHandler struct { @@ -28,20 +30,24 @@ type ReplayHandler struct { func (h ReplayHandler) Replay(ctx context.Context, req *pb.ReplayRequest) (*pb.ReplayResponse, error) { replayTenant, err := tenant.NewTenant(req.GetProjectName(), req.NamespaceName) if err != nil { + h.l.Error("invalid tenant information request project [%s] namespace [%s]: %s", req.GetProjectName(), req.GetNamespaceName(), err) return nil, errors.GRPCErr(err, "unable to start replay for "+req.GetJobName()) } jobName, err := scheduler.JobNameFrom(req.GetJobName()) if err != nil { + h.l.Error("error adapting job name [%s]: %s", req.GetJobName(), err) return nil, errors.GRPCErr(err, "unable to start replay for "+req.GetJobName()) } if err = req.GetStartTime().CheckValid(); err != nil { + h.l.Error("error validating start time: %s", err) return nil, errors.GRPCErr(errors.InvalidArgument(scheduler.EntityJobRun, "invalid start_time"), "unable to start replay for "+req.GetJobName()) } if req.GetEndTime() != nil { if err = req.GetEndTime().CheckValid(); err != nil { + h.l.Error("error validating end time: %s", err) return nil, errors.GRPCErr(errors.InvalidArgument(scheduler.EntityJobRun, "invalid end_time"), "unable to start replay for "+req.GetJobName()) } } @@ -50,6 +56,7 @@ func (h ReplayHandler) Replay(ctx context.Context, req *pb.ReplayRequest) (*pb.R if req.JobConfig != "" { jobConfig, err = parseJobConfig(req.JobConfig) if err != nil { + h.l.Error("error parsing job config: %s", err) return nil, errors.GRPCErr(err, "unable to parse replay job config for "+req.JobName) } } @@ -57,19 +64,88 @@ func (h ReplayHandler) Replay(ctx context.Context, req *pb.ReplayRequest) (*pb.R replayConfig := scheduler.NewReplayConfig(req.GetStartTime().AsTime(), req.GetEndTime().AsTime(), req.Parallel, jobConfig, req.Description) replayID, err := h.service.CreateReplay(ctx, replayTenant, jobName, replayConfig) if err != nil { + h.l.Error("error creating replay for job [%s]: %s", jobName, err) return nil, errors.GRPCErr(err, "unable to start replay for "+req.GetJobName()) } return &pb.ReplayResponse{Id: replayID.String()}, nil } +func (h ReplayHandler) ListReplay(ctx context.Context, req *pb.ListReplayRequest) (*pb.ListReplayResponse, error) { + projectName, err := tenant.ProjectNameFrom(req.GetProjectName()) + if err != nil { + h.l.Error("error adapting project name [%s]: %s", req.GetProjectName(), err) + return nil, errors.GRPCErr(err, "unable to get replay list for "+req.GetProjectName()) + } + + replays, err := h.service.GetReplayList(ctx, projectName) + if err != nil { + h.l.Error("error getting replay list for project [%s]: %s", projectName, err) + return nil, errors.GRPCErr(err, "unable to get replay list for "+req.GetProjectName()) + } + + replayProtos := make([]*pb.GetReplayResponse, len(replays)) + for i, replay := range replays { + replayProtos[i] = replayToProto(replay) + } + + return &pb.ListReplayResponse{Replays: replayProtos}, nil +} + +func (h ReplayHandler) GetReplay(ctx context.Context, req *pb.GetReplayRequest) (*pb.GetReplayResponse, error) { + id, err := uuid.Parse(req.GetReplayId()) + if err != nil { + h.l.Error("error parsing replay id [%s]: %s", req.GetReplayId(), err) + err = errors.InvalidArgument(scheduler.EntityReplay, err.Error()) + return nil, errors.GRPCErr(err, "unable to get replay for replayID "+req.GetReplayId()) + } + + replay, err := h.service.GetReplayByID(ctx, id) + if err != nil { + if errors.IsErrorType(err, errors.ErrNotFound) { + h.l.Warn("replay with id [%s] is not found", id.String()) + return &pb.GetReplayResponse{}, nil + } + h.l.Error("error getting replay with id [%s]: %s", id.String(), err) + return nil, errors.GRPCErr(err, "unable to get replay for replayID "+req.GetReplayId()) + } + + runs := make([]*pb.ReplayRun, len(replay.Runs)) + for i, run := range replay.Runs { + runs[i] = &pb.ReplayRun{ + ScheduledAt: timestamppb.New(run.ScheduledAt), + Status: run.State.String(), + } + } + + replayProto := replayToProto(replay.Replay) + replayProto.ReplayRuns = runs + + return replayProto, nil +} + +func replayToProto(replay *scheduler.Replay) *pb.GetReplayResponse { + return &pb.GetReplayResponse{ + Id: replay.ID().String(), + JobName: replay.JobName().String(), + Status: replay.UserState().String(), + ReplayConfig: &pb.ReplayConfig{ + StartTime: timestamppb.New(replay.Config().StartTime), + EndTime: timestamppb.New(replay.Config().EndTime), + Parallel: replay.Config().Parallel, + JobConfig: replay.Config().JobConfig, + Description: replay.Config().Description, + }, + } +} + func parseJobConfig(jobConfig string) (map[string]string, error) { configs := map[string]string{} for _, config := range strings.Split(jobConfig, ",") { keyValue := strings.Split(config, "=") valueLen := 2 if len(keyValue) != valueLen { - return nil, fmt.Errorf("error on job config value, %s", config) + return nil, errors.InvalidArgument(scheduler.EntityReplay, "error on job config value, "+config) } key := strings.TrimSpace(strings.ToUpper(keyValue[0])) value := keyValue[1] diff --git a/core/scheduler/handler/v1beta1/replay_test.go b/core/scheduler/handler/v1beta1/replay_test.go index 5d53b70727..65abf3408b 100644 --- a/core/scheduler/handler/v1beta1/replay_test.go +++ b/core/scheduler/handler/v1beta1/replay_test.go @@ -7,15 +7,16 @@ import ( "time" "github.com/google/uuid" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "google.golang.org/protobuf/types/known/timestamppb" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/scheduler/handler/v1beta1" - "github.com/odpf/optimus/core/tenant" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/scheduler/handler/v1beta1" + "github.com/raystack/optimus/core/tenant" + errs "github.com/raystack/optimus/internal/errors" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) func TestReplayHandler(t *testing.T) { @@ -196,6 +197,156 @@ func TestReplayHandler(t *testing.T) { assert.Nil(t, result) }) }) + + t.Run("GetReplayList", func(t *testing.T) { + t.Run("return error when project name is not provided", func(t *testing.T) { + service := new(mockReplayService) + defer service.AssertExpectations(t) + + replayHandler := v1beta1.NewReplayHandler(logger, service) + + req := &pb.ListReplayRequest{ + ProjectName: "", + } + + result, err := replayHandler.ListReplay(ctx, req) + assert.ErrorContains(t, err, "project name is empty") + assert.Nil(t, result) + }) + t.Run("return error when get replay list failed", func(t *testing.T) { + service := new(mockReplayService) + service.On("GetReplayList", ctx, tenant.ProjectName("project-test")).Return(nil, errors.New("some error")) + defer service.AssertExpectations(t) + + replayHandler := v1beta1.NewReplayHandler(logger, service) + + req := &pb.ListReplayRequest{ + ProjectName: "project-test", + } + + result, err := replayHandler.ListReplay(ctx, req) + assert.Error(t, err) + assert.Nil(t, result) + }) + t.Run("return empty list when no replay found in project", func(t *testing.T) { + service := new(mockReplayService) + service.On("GetReplayList", ctx, tenant.ProjectName("project-test")).Return([]*scheduler.Replay{}, nil) + defer service.AssertExpectations(t) + + replayHandler := v1beta1.NewReplayHandler(logger, service) + + req := &pb.ListReplayRequest{ + ProjectName: "project-test", + } + + result, err := replayHandler.ListReplay(ctx, req) + assert.NoError(t, err) + assert.Empty(t, result.Replays) + }) + t.Run("return replay list when success", func(t *testing.T) { + tnnt, _ := tenant.NewTenant("project-test", "ns-1") + startTimeStr := "2023-01-02T15:00:00Z" + startTime, _ := time.Parse(scheduler.ISODateFormat, startTimeStr) + endTime := startTime.Add(48 * time.Hour) + replayConfig := scheduler.NewReplayConfig(startTime, endTime, true, map[string]string{}, description) + + replay1 := scheduler.NewReplayRequest("sample-job-A", tnnt, replayConfig, scheduler.ReplayStateInProgress) + replay2 := scheduler.NewReplayRequest("sample-job-B", tnnt, replayConfig, scheduler.ReplayStateCreated) + replay3 := scheduler.NewReplayRequest("sample-job-C", tnnt, replayConfig, scheduler.ReplayStateFailed) + service := new(mockReplayService) + service.On("GetReplayList", ctx, tenant.ProjectName("project-test")).Return([]*scheduler.Replay{replay1, replay2, replay3}, nil) + defer service.AssertExpectations(t) + + replayHandler := v1beta1.NewReplayHandler(logger, service) + + req := &pb.ListReplayRequest{ + ProjectName: "project-test", + } + + result, err := replayHandler.ListReplay(ctx, req) + assert.NoError(t, err) + assert.Len(t, result.Replays, 3) + }) + }) + + t.Run("GetReplay", func(t *testing.T) { + t.Run("returns error when uuid is not valid", func(t *testing.T) { + replayHandler := v1beta1.NewReplayHandler(logger, nil) + + req := &pb.GetReplayRequest{ + ProjectName: projectName, + ReplayId: "invalid-id", + } + result, err := replayHandler.GetReplay(ctx, req) + assert.ErrorContains(t, err, "invalid UUID") + assert.Nil(t, result) + }) + t.Run("returns error when service get replay by id is failed", func(t *testing.T) { + service := new(mockReplayService) + defer service.AssertExpectations(t) + + replayID := uuid.New() + service.On("GetReplayByID", ctx, replayID).Return(nil, errors.New("internal error")) + + replayHandler := v1beta1.NewReplayHandler(logger, service) + + req := &pb.GetReplayRequest{ + ProjectName: projectName, + ReplayId: replayID.String(), + } + result, err := replayHandler.GetReplay(ctx, req) + assert.ErrorContains(t, err, "internal error") + assert.Nil(t, result) + }) + t.Run("returns empty if replay not exist", func(t *testing.T) { + service := new(mockReplayService) + defer service.AssertExpectations(t) + + replayID := uuid.New() + service.On("GetReplayByID", ctx, replayID).Return(nil, errs.NotFound("entity", "not found")) + + replayHandler := v1beta1.NewReplayHandler(logger, service) + + req := &pb.GetReplayRequest{ + ProjectName: projectName, + ReplayId: replayID.String(), + } + result, err := replayHandler.GetReplay(ctx, req) + assert.NoError(t, err) + assert.Empty(t, result) + }) + t.Run("returns success if replay is exist", func(t *testing.T) { + service := new(mockReplayService) + defer service.AssertExpectations(t) + + replayID := uuid.New() + tnnt, _ := tenant.NewTenant("project-test", "ns-1") + startTimeStr := "2023-01-02T15:00:00Z" + startTime, _ := time.Parse(scheduler.ISODateFormat, startTimeStr) + endTime := startTime.Add(48 * time.Hour) + replayConfig := scheduler.NewReplayConfig(startTime, endTime, true, map[string]string{}, description) + replay := scheduler.NewReplay(replayID, "sample-job-A", tnnt, replayConfig, scheduler.ReplayStateInProgress, startTime) + service.On("GetReplayByID", ctx, replayID).Return(&scheduler.ReplayWithRun{ + Replay: replay, + Runs: []*scheduler.JobRunStatus{ + { + ScheduledAt: startTime, + State: scheduler.StatePending, + }, + }, + }, nil) + + replayHandler := v1beta1.NewReplayHandler(logger, service) + + req := &pb.GetReplayRequest{ + ProjectName: projectName, + ReplayId: replayID.String(), + } + result, err := replayHandler.GetReplay(ctx, req) + assert.NoError(t, err) + assert.NotEmpty(t, result) + }) + }) } // mockReplayService is an autogenerated mock type for the ReplayService type @@ -203,6 +354,32 @@ type mockReplayService struct { mock.Mock } +// GetReplayByID provides a mock function with given fields: ctx, replayID +func (_m *mockReplayService) GetReplayByID(ctx context.Context, replayID uuid.UUID) (*scheduler.ReplayWithRun, error) { + ret := _m.Called(ctx, replayID) + + var r0 *scheduler.ReplayWithRun + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID) (*scheduler.ReplayWithRun, error)); ok { + return rf(ctx, replayID) + } + if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID) *scheduler.ReplayWithRun); ok { + r0 = rf(ctx, replayID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*scheduler.ReplayWithRun) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, uuid.UUID) error); ok { + r1 = rf(ctx, replayID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // CreateReplay provides a mock function with given fields: ctx, _a1, jobName, config func (_m *mockReplayService) CreateReplay(ctx context.Context, _a1 tenant.Tenant, jobName scheduler.JobName, config *scheduler.ReplayConfig) (uuid.UUID, error) { ret := _m.Called(ctx, _a1, jobName, config) @@ -225,3 +402,29 @@ func (_m *mockReplayService) CreateReplay(ctx context.Context, _a1 tenant.Tenant return r0, r1 } + +// GetReplayList provides a mock function with given fields: ctx, projectName +func (_m *mockReplayService) GetReplayList(ctx context.Context, projectName tenant.ProjectName) ([]*scheduler.Replay, error) { + ret := _m.Called(ctx, projectName) + + var r0 []*scheduler.Replay + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, tenant.ProjectName) ([]*scheduler.Replay, error)); ok { + return rf(ctx, projectName) + } + if rf, ok := ret.Get(0).(func(context.Context, tenant.ProjectName) []*scheduler.Replay); ok { + r0 = rf(ctx, projectName) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*scheduler.Replay) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, tenant.ProjectName) error); ok { + r1 = rf(ctx, projectName) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/core/scheduler/job.go b/core/scheduler/job.go index 197735e33d..1c4458e942 100644 --- a/core/scheduler/job.go +++ b/core/scheduler/job.go @@ -5,9 +5,9 @@ import ( "strings" "time" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" - "github.com/odpf/optimus/internal/models" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/models" ) type ( @@ -125,7 +125,6 @@ type JobMetadata struct { type Schedule struct { DependsOnPast bool - CatchUp bool StartDate time.Time EndDate *time.Time Interval string @@ -139,6 +138,18 @@ func (j *JobWithDetails) GetLabelsAsString() string { return strings.TrimRight(labels, ",") } +func (j *JobWithDetails) GetUniqueLabelValues() []string { + labelValues := []string{} + m := map[string]bool{} + for _, v := range j.JobMetadata.Labels { + if _, ok := m[v]; !ok { + labelValues = append(labelValues, v) + } + m[v] = true + } + return labelValues +} + type Retry struct { ExponentialBackoff bool Count int diff --git a/core/scheduler/job_run.go b/core/scheduler/job_run.go index 25304e1afd..015d68e3ed 100644 --- a/core/scheduler/job_run.go +++ b/core/scheduler/job_run.go @@ -5,8 +5,8 @@ import ( "github.com/google/uuid" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" ) type JobRunID uuid.UUID @@ -35,12 +35,13 @@ func (i JobRunID) IsEmpty() bool { type JobRun struct { ID uuid.UUID - JobName JobName - Tenant tenant.Tenant - State State - StartTime time.Time - SLAAlert bool - EndTime time.Time + JobName JobName + Tenant tenant.Tenant + State State + ScheduledAt time.Time + StartTime time.Time + SLAAlert bool + EndTime time.Time Monitoring map[string]any } @@ -61,3 +62,9 @@ type NotifyAttrs struct { Route string Secret string } + +const ( + MetricNotificationQueue = "notification_queue_total" + MetricNotificationWorkerBatch = "notification_worker_batch_total" + MetricNotificationWorkerSendErr = "notification_worker_send_err_total" +) diff --git a/core/scheduler/job_test.go b/core/scheduler/job_test.go index 5f5bca9d78..c77375625d 100644 --- a/core/scheduler/job_test.go +++ b/core/scheduler/job_test.go @@ -6,8 +6,8 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/tenant" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/tenant" ) func TestJob(t *testing.T) { @@ -136,6 +136,20 @@ func TestJob(t *testing.T) { labels := jobWithDetails.GetLabelsAsString() assert.Equal(t, labels, "label1=someVale") }) + t.Run("GetUniqueLabelValues", func(t *testing.T) { + jobWithDetails := scheduler.JobWithDetails{ + Name: "jobName", + JobMetadata: &scheduler.JobMetadata{ + Labels: map[string]string{ + "label1": "someVale", + "label2": "someVale", + "label3": "another", + }, + }, + } + labels := jobWithDetails.GetUniqueLabelValues() + assert.ElementsMatch(t, labels, []string{"someVale", "another"}) + }) t.Run("GroupJobsByTenant", func(t *testing.T) { t1, _ := tenant.NewTenant("proj", "ns1") t2, _ := tenant.NewTenant("proj", "ns1") diff --git a/core/scheduler/replay.go b/core/scheduler/replay.go index 8cd3b1942f..fdce7c3f0e 100644 --- a/core/scheduler/replay.go +++ b/core/scheduler/replay.go @@ -1,29 +1,43 @@ package scheduler import ( - "sort" "strings" "time" "github.com/google/uuid" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" ) const ( + // initial state ReplayStateCreated ReplayState = "created" + // running state ReplayStateInProgress ReplayState = "in progress" - ReplayStateInvalid ReplayState = "invalid" ReplayStatePartialReplayed ReplayState = "partial replayed" ReplayStateReplayed ReplayState = "replayed" + // terminal state + ReplayStateInvalid ReplayState = "invalid" ReplayStateSuccess ReplayState = "success" ReplayStateFailed ReplayState = "failed" + + // state on presentation layer + ReplayUserStateCreated ReplayUserState = "created" + ReplayUserStateInProgress ReplayUserState = "in progress" + ReplayUserStateInvalid ReplayUserState = "invalid" + ReplayUserStateSuccess ReplayUserState = "success" + ReplayUserStateFailed ReplayUserState = "failed" + + EntityReplay = "replay" ) -type ReplayState string +type ( + ReplayState string // contract status for business layer + ReplayUserState string // contract status for presentation layer +) func ReplayStateFromString(state string) (ReplayState, error) { switch strings.ToLower(state) { @@ -50,6 +64,10 @@ func (j ReplayState) String() string { return string(j) } +func (j ReplayUserState) String() string { + return string(j) +} + type Replay struct { id uuid.UUID @@ -83,6 +101,23 @@ func (r *Replay) State() ReplayState { return r.state } +func (r *Replay) UserState() ReplayUserState { + switch r.state { + case ReplayStateCreated: + return ReplayUserStateCreated + case ReplayStateInProgress, ReplayStatePartialReplayed, ReplayStateReplayed: + return ReplayUserStateInProgress + case ReplayStateInvalid: + return ReplayUserStateInvalid + case ReplayStateSuccess: + return ReplayUserStateSuccess + case ReplayStateFailed: + return ReplayUserStateFailed + default: + return "" + } +} + func (r *Replay) Message() string { return r.message } @@ -105,17 +140,19 @@ type ReplayWithRun struct { } func (r *ReplayWithRun) GetFirstExecutableRun() *JobRunStatus { - sort.Slice(r.Runs, func(i, j int) bool { - return r.Runs[i].ScheduledAt.Before(r.Runs[j].ScheduledAt) - }) - return r.Runs[0] + runs := JobRunStatusList(r.Runs).GetSortedRunsByStates([]State{StatePending}) + if len(runs) > 0 { + return runs[0] + } + return nil } func (r *ReplayWithRun) GetLastExecutableRun() *JobRunStatus { - sort.Slice(r.Runs, func(i, j int) bool { - return r.Runs[i].ScheduledAt.After(r.Runs[j].ScheduledAt) - }) - return r.Runs[0] + runs := JobRunStatusList(r.Runs).GetSortedRunsByStates([]State{StatePending}) + if len(runs) > 0 { + return runs[len(runs)-1] + } + return nil } type ReplayConfig struct { diff --git a/core/scheduler/replay_test.go b/core/scheduler/replay_test.go index d30e0cde74..8c9a2436c6 100644 --- a/core/scheduler/replay_test.go +++ b/core/scheduler/replay_test.go @@ -7,8 +7,8 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/tenant" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/tenant" ) func TestReplay(t *testing.T) { @@ -26,6 +26,8 @@ func TestReplay(t *testing.T) { scheduledTimeStr1 := "2023-01-02T12:00:00Z" scheduledTime1, _ := time.Parse(scheduler.ISODateFormat, scheduledTimeStr1) scheduledTime2 := scheduledTime1.Add(24 * time.Hour) + scheduledTime3 := scheduledTime1.Add(2 * 24 * time.Hour) + scheduledTime4 := scheduledTime1.Add(3 * 24 * time.Hour) t.Run("NewReplay", func(t *testing.T) { createdTime := time.Now() @@ -54,12 +56,20 @@ func TestReplay(t *testing.T) { t.Run("ReplayWithRun", func(t *testing.T) { firstRun := &scheduler.JobRunStatus{ ScheduledAt: scheduledTime1, - State: scheduler.StatePending, + State: scheduler.StateInProgress, } secondRun := &scheduler.JobRunStatus{ ScheduledAt: scheduledTime2, State: scheduler.StatePending, } + thirdRun := &scheduler.JobRunStatus{ + ScheduledAt: scheduledTime3, + State: scheduler.StatePending, + } + fourthRun := &scheduler.JobRunStatus{ + ScheduledAt: scheduledTime4, + State: scheduler.StateInProgress, + } t.Run("GetFirstExecutableRun", func(t *testing.T) { replay := scheduler.NewReplay(replayID, jobNameA, tnnt, replayConfig, scheduler.ReplayStateCreated, time.Now()) @@ -68,10 +78,12 @@ func TestReplay(t *testing.T) { Runs: []*scheduler.JobRunStatus{ firstRun, secondRun, + thirdRun, + fourthRun, }, } firstExecutableRun := replayWithRun.GetFirstExecutableRun() - assert.Equal(t, firstRun, firstExecutableRun) + assert.Equal(t, firstExecutableRun, secondRun) }) t.Run("GetLastExecutableRun", func(t *testing.T) { replay := scheduler.NewReplay(replayID, jobNameA, tnnt, replayConfig, scheduler.ReplayStateCreated, time.Now()) @@ -80,10 +92,12 @@ func TestReplay(t *testing.T) { Runs: []*scheduler.JobRunStatus{ firstRun, secondRun, + thirdRun, + fourthRun, }, } lastExecutableRun := replayWithRun.GetLastExecutableRun() - assert.Equal(t, secondRun, lastExecutableRun) + assert.Equal(t, lastExecutableRun, thirdRun) }) }) diff --git a/core/scheduler/resolver/priority_resolver.go b/core/scheduler/resolver/priority_resolver.go deleted file mode 100644 index f02e318142..0000000000 --- a/core/scheduler/resolver/priority_resolver.go +++ /dev/null @@ -1,144 +0,0 @@ -package resolver - -import ( - "context" - "errors" - "fmt" - - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/internal/lib/tree" -) - -const ( - // MinPriorityWeight - what's the minimum weight that we want to give to a DAG. - // airflow also sets the default priority weight as 1 - MinPriorityWeight = 1 - - // MaxPriorityWeight - is the maximus weight a DAG will be given. - MaxPriorityWeight = 10000 - - // PriorityWeightGap - while giving weights to the DAG, what's the GAP - // do we want to consider. PriorityWeightGap = 1 means, weights will be 1, 2, 3 etc. - PriorityWeightGap = 10 -) - -// ErrPriorityNotFound is thrown when priority of a given spec is not found -var ErrPriorityNotFound = errors.New("priority weight not found") - -// PriorityResolver runs a breadth first traversal on DAG/Job dependencies trees -// and returns highest weight for the DAG that do not have any dependencies, dynamically. -// eg, consider following DAGs and dependencies: [dag1 <- dag2 <- dag3] [dag4] [dag5 <- dag6] -// In this example, we've 6 DAGs in which dag1, dag2, dag5 have dependent DAGs. which means, -// It'd be preferable to run dag1, dag4, dag5 before other DAGs. Results would be: -// dag1, dag4, dag5 will get highest weight (maxWeight) -// dag2, dag6 will get weight of maxWeight-1 -// dag3 will get maxWeight-2 -// Note: it's crucial that dependencies of all Jobs are already resolved -type PriorityResolver struct{} - -// NewPriorityResolver create an instance of PriorityResolver -func NewPriorityResolver() *PriorityResolver { - return &PriorityResolver{} -} - -// Resolve takes jobsWithDetails and returns them with resolved priorities -func (a *PriorityResolver) Resolve(_ context.Context, jobWithDetails []*scheduler.JobWithDetails) error { - if err := a.resolvePriorities(jobWithDetails); err != nil { - return fmt.Errorf("error occurred while resolving priority: %w", err) - } - return nil -} - -// resolvePriorities resolves priorities of all provided jobs -func (a *PriorityResolver) resolvePriorities(jobsWithDetails []*scheduler.JobWithDetails) error { - // build a multi-root tree from all jobs based on their dependencies - multiRootTree, err := a.buildMultiRootDependencyTree(jobsWithDetails) - if err != nil { - return err - } - - // perform a breadth first traversal on all trees and assign weights. - // higher weights are assign to the nodes on the top, and the weight - // reduces as we go down the tree level - taskPriorityMap := map[string]int{} - a.assignWeight(multiRootTree.GetRootNodes(), MaxPriorityWeight, taskPriorityMap) - - for _, jobWithDetails := range jobsWithDetails { - priority, ok := taskPriorityMap[jobWithDetails.Name.String()] - if !ok { - return fmt.Errorf("%s: %w", jobWithDetails.Name, ErrPriorityNotFound) - } - jobWithDetails.Priority = priority - } - - return nil -} - -func (a *PriorityResolver) assignWeight(rootNodes []*tree.TreeNode, weight int, taskPriorityMap map[string]int) { - subChildren := []*tree.TreeNode{} - for _, rootNode := range rootNodes { - taskPriorityMap[rootNode.GetName()] = weight - subChildren = append(subChildren, rootNode.Dependents...) - } - if len(subChildren) > 0 { - a.assignWeight(subChildren, weight-PriorityWeightGap, taskPriorityMap) - } -} - -// buildMultiRootDependencyTree - converts []jobWithDetails into a MultiRootTree -// based on the dependencies of each DAG. -func (a *PriorityResolver) buildMultiRootDependencyTree(jobsWithDetails []*scheduler.JobWithDetails) (*tree.MultiRootTree, error) { - // creates map[jobName]jobWithDetails for faster retrieval - jobWithDetailsMap := make(map[string]*scheduler.JobWithDetails) - for _, dagSpec := range jobsWithDetails { - jobWithDetailsMap[dagSpec.Name.String()] = dagSpec - } - - // build a multi root tree and assign dependencies - // ignore any other dependency apart from intra-tenant - multiRootTree := tree.NewMultiRootTree() - for _, childSpec := range jobWithDetailsMap { - childNode := a.findOrCreateDAGNode(multiRootTree, childSpec) - for _, upstream := range childSpec.Upstreams.UpstreamJobs { - missingParent := false - parentSpec, ok := jobWithDetailsMap[upstream.JobName] - if !ok { - // when the dependency of a jobWithDetails belong to some other tenant or is external, the jobWithDetails won't - // be available in jobsWithDetails []job_run.jobWithDetails object (which is tenant specific) - // so we'll add a dummy jobWithDetails for that cross tenant/external dependency. - parentSpec = &scheduler.JobWithDetails{ - Name: scheduler.JobName(upstream.JobName), - Upstreams: scheduler.Upstreams{}, - } - missingParent = true - } - parentNode := a.findOrCreateDAGNode(multiRootTree, parentSpec) - parentNode.AddDependent(childNode) - multiRootTree.AddNode(parentNode) - if missingParent { - // dependency that are outside current project will be considered as root because - // optimus don't know dependencies of those external parents - multiRootTree.MarkRoot(parentNode) - } - } - - // the DAGs with no dependencies are root nodes for the tree - if len(childSpec.Upstreams.UpstreamJobs) == 0 { - multiRootTree.MarkRoot(childNode) - } - } - - if _, err := multiRootTree.ValidateCyclic(); err != nil { - return nil, err - } - return multiRootTree, nil -} - -func (*PriorityResolver) findOrCreateDAGNode(dagTree *tree.MultiRootTree, jobDetails *scheduler.JobWithDetails) *tree.TreeNode { - node, ok := dagTree.GetNodeByName(jobDetails.Name.String()) - if !ok { - node = tree.NewTreeNode(jobDetails) - dagTree.AddNode(node) - } - return node -} diff --git a/core/scheduler/resolver/priority_resolver_test.go b/core/scheduler/resolver/priority_resolver_test.go deleted file mode 100644 index c8f9e06d5c..0000000000 --- a/core/scheduler/resolver/priority_resolver_test.go +++ /dev/null @@ -1,463 +0,0 @@ -package resolver_test - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/scheduler/resolver" - "github.com/odpf/optimus/internal/lib/tree" -) - -func getJobWithUpstream(name string, upstreams ...string) *scheduler.JobWithDetails { - var jobUpstreams []*scheduler.JobUpstream - for _, n := range upstreams { - jobUpstreams = append(jobUpstreams, &scheduler.JobUpstream{ - JobName: n, - }) - } - return &scheduler.JobWithDetails{ - Name: scheduler.JobName(name), - Upstreams: scheduler.Upstreams{UpstreamJobs: jobUpstreams}, - } -} - -func TestPriorityWeightResolver(t *testing.T) { - // noDependency := job_run.Upstreams{} - ctx := context.Background() - - t.Run("Resolve should assign correct weights to the DAGs with mentioned dependencies", func(t *testing.T) { - spec1 := "dag1-no-deps" - spec2 := "dag2-deps-on-dag1" - spec3 := "dag3-deps-on-dag2" - spec4 := "dag4-no-deps" - spec5 := "dag5-deps-on-dag1" - spec6 := "dag6-no-deps" - spec7 := "dag7-deps-on-dag6" - spec8 := "dag8-no-deps" - spec9 := "dag9-deps-on-dag8" - spec10 := "dag10-deps-on-dag9" - spec11 := "dag11-deps-on-dag10" - - s1 := getJobWithUpstream(spec1) - s2 := getJobWithUpstream(spec2, spec1) - s3 := getJobWithUpstream(spec3, spec2) - s4 := getJobWithUpstream(spec4) - s5 := getJobWithUpstream(spec5, spec1) - s6 := getJobWithUpstream(spec6) - s7 := getJobWithUpstream(spec7, spec6) - s8 := getJobWithUpstream(spec8) - s9 := getJobWithUpstream(spec9, spec8) - s10 := getJobWithUpstream(spec10, spec9) - s11 := getJobWithUpstream(spec11, spec10) - - dagSpec := []*scheduler.JobWithDetails{s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11} - assigner := resolver.NewPriorityResolver() - err := assigner.Resolve(ctx, dagSpec) - assert.Nil(t, err) - - max := resolver.MaxPriorityWeight - max1 := max - resolver.PriorityWeightGap*1 - max2 := max - resolver.PriorityWeightGap*2 - max3 := max - resolver.PriorityWeightGap*3 - expectedWeights := map[scheduler.JobName]int{ - s1.Name: max, s2.Name: max1, s3.Name: max2, s4.Name: max, s5.Name: max1, - s6.Name: max, s7.Name: max1, s8.Name: max, s9.Name: max1, s10.Name: max2, s11.Name: max3, - } - - for _, jobSpec := range dagSpec { - assert.Equal(t, expectedWeights[jobSpec.Name], jobSpec.Priority) - } - }) - - t.Run("Resolve should assign correct weights to the DAGs with mentioned dependencies", func(t *testing.T) { - // run the test multiple times - for i := 1; i < 10; i++ { - spec1 := "dag1" - spec11 := "dag1-1" - spec12 := "dag1-2" - spec111 := "dag1-1-1" - spec112 := "dag1-1-2" - spec121 := "dag1-2-1" - spec122 := "dag1-2-2" - s1 := getJobWithUpstream(spec1) - s11 := getJobWithUpstream(spec11, spec1) - s12 := getJobWithUpstream(spec12, spec1) - s111 := getJobWithUpstream(spec111, spec11) - s112 := getJobWithUpstream(spec112, spec12) - s121 := getJobWithUpstream(spec121, spec12) - s122 := getJobWithUpstream(spec122, spec12) - - spec2 := "dag2" - spec21 := "dag2-1" - spec22 := "dag2-2" - spec211 := "dag2-1-1" - spec212 := "dag2-1-2" - spec221 := "dag2-2-1" - spec222 := "dag2-2-2" - s2 := getJobWithUpstream(spec2) - s21 := getJobWithUpstream(spec21, spec2) - s22 := getJobWithUpstream(spec22, spec2) - s211 := getJobWithUpstream(spec211, spec21) - s212 := getJobWithUpstream(spec212, spec21) - s221 := getJobWithUpstream(spec221, spec22) - s222 := getJobWithUpstream(spec222, spec22) - - dagSpec := []*scheduler.JobWithDetails{s1, s11, s12, s111, s112, s121, s122, s2, s21, s22, s211, s212, s221, s222} - - assigner := resolver.NewPriorityResolver() - err := assigner.Resolve(ctx, dagSpec) - assert.Nil(t, err) - - max := resolver.MaxPriorityWeight - max1 := max - resolver.PriorityWeightGap*1 - max2 := max - resolver.PriorityWeightGap*2 - expectedWeights := map[string]int{ - spec1: max, spec11: max1, spec12: max1, spec111: max2, spec112: max2, spec121: max2, spec122: max2, - spec2: max, spec21: max1, spec22: max1, spec211: max2, spec212: max2, spec221: max2, spec222: max2, - } - - for _, jobSpec := range dagSpec { - assert.Equal(t, expectedWeights[jobSpec.Name.String()], jobSpec.Priority) - } - } - }) - - t.Run("Resolve should assign correct weights to the DAGs with mentioned dependencies", func(t *testing.T) { - spec1 := "dag1-no-deps" - spec2 := "dag2-deps-on-dag1" - spec3 := "dag3-deps-on-dag2" - spec4 := "dag4-no-deps" - spec5 := "dag5-deps-on-dag1" - - s1 := getJobWithUpstream(spec1) - s2 := getJobWithUpstream(spec2, spec1) - s3 := getJobWithUpstream(spec3, spec2) - s4 := getJobWithUpstream(spec4) - s5 := getJobWithUpstream(spec5, spec1) - dagSpec := []*scheduler.JobWithDetails{s1, s2, s3, s4, s5} - assigner := resolver.NewPriorityResolver() - err := assigner.Resolve(ctx, dagSpec) - assert.Nil(t, err) - - max := resolver.MaxPriorityWeight - max1 := max - resolver.PriorityWeightGap*1 - max2 := max - resolver.PriorityWeightGap*2 - expectedWeights := map[string]int{spec1: max, spec2: max1, spec3: max2, spec4: max, spec5: max1} - - for _, jobSpec := range dagSpec { - assert.Equal(t, expectedWeights[jobSpec.Name.String()], jobSpec.Priority) - } - }) - - t.Run("Resolve should fail when circular dependency is detected (atleast one DAG with no dependency)", func(t *testing.T) { - spec1 := "dag1-no-deps" - spec2 := "dag2-deps-on-dag1" - spec3 := "dag3-deps-on-dag2" - - s1 := getJobWithUpstream(spec1) - s3 := getJobWithUpstream(spec3, spec2, spec1) - s2 := getJobWithUpstream(spec2, spec3, spec1) - - dagSpec := []*scheduler.JobWithDetails{s1, s2, s3} - - assigner := resolver.NewPriorityResolver() - err := assigner.Resolve(ctx, dagSpec) - assert.Contains(t, err.Error(), "error occurred while resolving priority:") - assert.Contains(t, err.Error(), tree.ErrCyclicDependencyEncountered.Error()) - }) - - t.Run("resolve should give error on Cyclic dependency", func(t *testing.T) { - spec2 := "dag2-deps-on-dag1" - spec3 := "dag3-deps-on-dag2" - - s3 := getJobWithUpstream(spec3, spec2) - s2 := getJobWithUpstream(spec2, spec3) - - dagSpec := []*scheduler.JobWithDetails{s2, s3} - - assigner := resolver.NewPriorityResolver() - err := assigner.Resolve(ctx, dagSpec) - assert.NotNil(t, err) - assert.Contains(t, err.Error(), tree.ErrCyclicDependencyEncountered.Error()) - }) - - t.Run("Resolve should assign correct weights (maxWeight) with no dependencies", func(t *testing.T) { - spec1 := "dag1-no-deps" - spec2 := "dag4-no-deps" - - s1 := getJobWithUpstream(spec1) - s2 := getJobWithUpstream(spec2) - dagSpec := []*scheduler.JobWithDetails{s1, s2} - - assigner := resolver.NewPriorityResolver() - err := assigner.Resolve(ctx, dagSpec) - assert.Nil(t, err) - - max := resolver.MaxPriorityWeight - - for _, jobSpec := range dagSpec { - assert.Equal(t, max, jobSpec.Priority) - } - }) - - t.Run("Resolve should assign correct weight to single DAG", func(t *testing.T) { - spec1 := "dag1-no-deps" - s1 := getJobWithUpstream(spec1) - dagSpec := []*scheduler.JobWithDetails{s1} - - assigner := resolver.NewPriorityResolver() - err := assigner.Resolve(ctx, dagSpec) - assert.Nil(t, err) - - for _, jobSpec := range dagSpec { - assert.Equal(t, resolver.MaxPriorityWeight, jobSpec.Priority) - } - }) -} - -func TestDAGNode(t *testing.T) { - t.Run("TreeNode should handle all TreeNode operations", func(t *testing.T) { - dagSpec := &scheduler.JobWithDetails{Name: "testdag"} - dagSpec2 := &scheduler.JobWithDetails{Name: "testdag"} - dagSpec3 := &scheduler.JobWithDetails{Name: "testdag"} - - node := tree.NewTreeNode(dagSpec) - node2 := tree.NewTreeNode(dagSpec2) - node3 := tree.NewTreeNode(dagSpec3) - - assert.Equal(t, "testdag", node.GetName()) - assert.Equal(t, []*tree.TreeNode{}, node.Dependents) - - node.AddDependent(node2) - assert.Equal(t, 1, len(node.Dependents)) - - node.AddDependent(node3) - assert.Equal(t, 2, len(node.Dependents)) - - node2.AddDependent(node3) - assert.Equal(t, 1, len(node2.Dependents)) - assert.Equal(t, 0, len(node3.Dependents)) - }) -} - -func TestMultiRootDAGTree(t *testing.T) { - t.Run("should handle all NewMultiRootTree operations", func(t *testing.T) { - dagSpec1 := &scheduler.JobWithDetails{Name: "testdag1"} - dagSpec2 := &scheduler.JobWithDetails{Name: "testdag2"} - dagSpec3 := &scheduler.JobWithDetails{Name: "testdag3"} - - node1 := tree.NewTreeNode(dagSpec1) - node2 := tree.NewTreeNode(dagSpec2) - node3 := tree.NewTreeNode(dagSpec3) - node4 := tree.NewTreeNode(dagSpec2) - - node2.AddDependent(node3) - node1.AddDependent(node1) - - dagTree := tree.NewMultiRootTree() - - // non-existing node should return nil, and ok=false - n, ok := dagTree.GetNodeByName("non-existing") - assert.False(t, ok) - assert.Nil(t, n) - - // should return the node, when an existing node is requested by name - dagTree.AddNode(node1) - n, ok = dagTree.GetNodeByName(node1.GetName()) - assert.True(t, ok) - assert.Equal(t, dagSpec1.Name.String(), n.GetName()) - assert.Equal(t, []*tree.TreeNode{}, dagTree.GetRootNodes()) - - // should return root nodes, when added as root - dagTree.MarkRoot(node1) - assert.Equal(t, []*tree.TreeNode{node1}, dagTree.GetRootNodes()) - - // adding nodes should maintain the dependency relationship - dagTree.AddNode(node2) - dagTree.AddNodeIfNotExist(node3) - - n, _ = dagTree.GetNodeByName(node1.GetName()) - assert.Equal(t, 1, len(n.Dependents)) - - n, _ = dagTree.GetNodeByName(node2.GetName()) - assert.Equal(t, 1, len(n.Dependents)) - - n, _ = dagTree.GetNodeByName(node3.GetName()) - assert.Equal(t, 0, len(n.Dependents)) - - // AddNodeIfNotExist should not break the tree - dagTree.AddNodeIfNotExist(node3) - n, _ = dagTree.GetNodeByName(node3.GetName()) - assert.Equal(t, 0, len(n.Dependents)) - - // AddNodeIfNotExist should not break the tree even when a new node - // with same name is added - dagTree.AddNodeIfNotExist(node4) - n, _ = dagTree.GetNodeByName(node1.GetName()) - assert.Equal(t, 1, len(n.Dependents)) - - // AddNode should break the tree if a node with same name is added - // since node4 and node2 has same name. and node 4 has no deps. - // it should replace node2 and break the tree - dagTree.AddNode(node4) - n, ok = dagTree.GetNodeByName(node2.GetName()) - assert.Equal(t, 0, len(n.Dependents)) - assert.Equal(t, true, ok) - }) - - t.Run("should detect any cycle in the tree", func(t *testing.T) { - dagSpec1 := &scheduler.JobWithDetails{Name: "testdag1"} - dagSpec2 := &scheduler.JobWithDetails{Name: "testdag2"} - dagSpec3 := &scheduler.JobWithDetails{Name: "testdag3"} - - node1 := tree.NewTreeNode(dagSpec1) - node2 := tree.NewTreeNode(dagSpec2) - node3 := tree.NewTreeNode(dagSpec3) - - node1.AddDependent(node2) - node2.AddDependent(node3) - node3.AddDependent(node2) - - dagTree := tree.NewMultiRootTree() - dagTree.AddNode(node1) - dagTree.MarkRoot(node1) - dagTree.AddNode(node2) - dagTree.AddNode(node3) - - _, err := dagTree.ValidateCyclic() - assert.NotNil(t, err) - assert.Contains(t, err.Error(), tree.ErrCyclicDependencyEncountered.Error()) - }) - - t.Run("should create tree with multi level dependencies", func(t *testing.T) { - d1 := &scheduler.JobWithDetails{Name: "d1"} - d11 := &scheduler.JobWithDetails{Name: "d11"} - d12 := &scheduler.JobWithDetails{Name: "d12"} - - d111 := &scheduler.JobWithDetails{Name: "d111"} - d112 := &scheduler.JobWithDetails{Name: "d112"} - d121 := &scheduler.JobWithDetails{Name: "d121"} - d122 := &scheduler.JobWithDetails{Name: "d122"} - - d1211 := &scheduler.JobWithDetails{Name: "d1211"} - d1212 := &scheduler.JobWithDetails{Name: "d1212"} - - dagTree := tree.NewMultiRootTree() - - dagTree.AddNodeIfNotExist(tree.NewTreeNode(d1211)) - dagTree.AddNodeIfNotExist(tree.NewTreeNode(d1212)) - - dagTree.AddNodeIfNotExist(tree.NewTreeNode(d11)) - dagTree.AddNodeIfNotExist(tree.NewTreeNode(d12)) - - dagTree.AddNodeIfNotExist(tree.NewTreeNode(d111)) - dagTree.AddNodeIfNotExist(tree.NewTreeNode(d121)) - dagTree.AddNodeIfNotExist(tree.NewTreeNode(d122)) - - node111, _ := dagTree.GetNodeByName(d111.Name.String()) - node112, _ := dagTree.GetNodeByName(d112.Name.String()) - if node112 == nil { - node112 = tree.NewTreeNode(d112) - dagTree.AddNode(tree.NewTreeNode(d112)) - } - node121, _ := dagTree.GetNodeByName(d121.Name.String()) - node122, _ := dagTree.GetNodeByName(d122.Name.String()) - - node11, _ := dagTree.GetNodeByName(d11.Name.String()) - node12, _ := dagTree.GetNodeByName(d12.Name.String()) - - node1 := tree.NewTreeNode(d1) - node1.AddDependent(node11).AddDependent(node12) - dagTree.AddNode(node1) - dagTree.MarkRoot(node1) - - node11.AddDependent(node111).AddDependent(node112) - dagTree.AddNode(node11) - - node12.AddDependent(node121).AddDependent(node122) - dagTree.AddNode(node12) - - node1211, _ := dagTree.GetNodeByName(d1211.Name.String()) - node1212, _ := dagTree.GetNodeByName(d1212.Name.String()) - node121.AddDependent(node1211).AddDependent(node1212) - dagTree.AddNode(node121) - dagTree.AddNode(node1211) - dagTree.AddNode(node1212) - - _, err := dagTree.ValidateCyclic() - assert.Nil(t, err) - - depsMap := map[*tree.TreeNode]int{ - node1: 2, - node11: 2, node12: 2, - node111: 0, node112: 0, node121: 2, node122: 0, - node1211: 0, node1212: 0, - } - - for node, depCount := range depsMap { - n, ok := dagTree.GetNodeByName(node.GetName()) - assert.True(t, ok) - assert.Equal(t, depCount, len(n.Dependents)) - } - }) - - t.Run("should not have cycles if only one node with no dependency is in the tree", func(t *testing.T) { - dagSpec2 := &scheduler.JobWithDetails{Name: "testdag2"} - node2 := tree.NewTreeNode(dagSpec2) - - dagTree := tree.NewMultiRootTree() - dagTree.AddNode(node2) - dagTree.MarkRoot(node2) - - _, err := dagTree.ValidateCyclic() - assert.Nil(t, err) - }) - - t.Run("should not have cycles in a tree with no root", func(t *testing.T) { - dagSpec2 := &scheduler.JobWithDetails{Name: "testdag2"} - node2 := tree.NewTreeNode(dagSpec2) - - dagTree := tree.NewMultiRootTree() - dagTree.AddNode(node2) - - _, err := dagTree.ValidateCyclic() - assert.Nil(t, err) - }) - - t.Run("should detect any cycle in the tree with multiple sub trees", func(t *testing.T) { - node1 := tree.NewTreeNode(&scheduler.JobWithDetails{Name: "testdag1"}) - node2 := tree.NewTreeNode(&scheduler.JobWithDetails{Name: "testdag2"}) - node3 := tree.NewTreeNode(&scheduler.JobWithDetails{Name: "testdag3"}) - node1.AddDependent(node2) - node2.AddDependent(node3) - - node11 := tree.NewTreeNode(&scheduler.JobWithDetails{Name: "testdag11"}) - node21 := tree.NewTreeNode(&scheduler.JobWithDetails{Name: "testdag21"}) - node31 := tree.NewTreeNode(&scheduler.JobWithDetails{Name: "testdag31"}) - node41 := tree.NewTreeNode(&scheduler.JobWithDetails{Name: "testdag41"}) - node11.AddDependent(node21) - node21.AddDependent(node31) - node31.AddDependent(node11) // causing cyclic dep - node31.AddDependent(node41) - node41.AddDependent(node21) - - dagTree := tree.NewMultiRootTree() - dagTree.AddNode(node1) - dagTree.MarkRoot(node1) - dagTree.AddNode(node2) - dagTree.AddNode(node3) - - dagTree.AddNode(node11) - dagTree.AddNode(node21) - dagTree.MarkRoot(node21) - dagTree.AddNode(node31) - dagTree.AddNode(node41) - - _, err := dagTree.ValidateCyclic() - assert.NotNil(t, err) - assert.Contains(t, err.Error(), tree.ErrCyclicDependencyEncountered.Error()) - }) -} diff --git a/core/scheduler/resolver/simple_resolver.go b/core/scheduler/resolver/simple_resolver.go new file mode 100644 index 0000000000..06c0dae6f5 --- /dev/null +++ b/core/scheduler/resolver/simple_resolver.go @@ -0,0 +1,41 @@ +package resolver + +import ( + "context" + + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/tenant" +) + +const ( + // maxPriorityWeight - is the maximus weight a DAG will be given. + maxPriorityWeight = 10000 + + // priorityWeightGap - while giving weights to the DAG, what's the GAP + // do we want to consider. PriorityWeightGap = 1 means, weights will be 1, 2, 3 etc. + priorityWeightGap = 10 +) + +type SimpleResolver struct{} + +func NewSimpleResolver() *SimpleResolver { + return &SimpleResolver{} +} + +func (SimpleResolver) Resolve(_ context.Context, details []*scheduler.JobWithDetails) error { // nolint:unparam + for _, job := range details { + priority := maxPriorityWeight - numberOfUpstreams(job.Upstreams, job.Job.Tenant)*priorityWeightGap + job.Priority = priority + } + return nil +} + +func numberOfUpstreams(upstream scheduler.Upstreams, tnnt tenant.Tenant) int { + count := 0 + for _, u := range upstream.UpstreamJobs { + if u.Tenant == tnnt { + count++ + } + } + return count +} diff --git a/core/scheduler/resolver/simple_resolver_test.go b/core/scheduler/resolver/simple_resolver_test.go new file mode 100644 index 0000000000..13e99739ed --- /dev/null +++ b/core/scheduler/resolver/simple_resolver_test.go @@ -0,0 +1,76 @@ +package resolver_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/scheduler/resolver" + "github.com/raystack/optimus/core/tenant" +) + +func TestSimpleResolver(t *testing.T) { + ctx := context.Background() + tnnt1, _ := tenant.NewTenant("test-proj", "test-ns") + + t.Run("returns max priority for root node", func(t *testing.T) { + j1 := &scheduler.JobWithDetails{ + Name: scheduler.JobName("RootNode"), + Job: &scheduler.Job{Tenant: tnnt1}, + Upstreams: scheduler.Upstreams{ + UpstreamJobs: nil, + }, + } + + s1 := resolver.SimpleResolver{} + err := s1.Resolve(ctx, []*scheduler.JobWithDetails{j1}) + assert.NoError(t, err) + assert.Equal(t, 10000, j1.Priority) + }) + t.Run("returns max priority for upstream not in same tenant", func(t *testing.T) { + tnnt2, _ := tenant.NewTenant("proj2", "namespace2") + upstream := &scheduler.JobUpstream{ + JobName: "upstreamInOtherTenant", + Tenant: tnnt2, + } + + j1 := &scheduler.JobWithDetails{ + Name: scheduler.JobName("RootNode"), + Job: &scheduler.Job{Tenant: tnnt1}, + Upstreams: scheduler.Upstreams{ + UpstreamJobs: []*scheduler.JobUpstream{upstream}, + }, + } + + s1 := resolver.SimpleResolver{} + err := s1.Resolve(ctx, []*scheduler.JobWithDetails{j1}) + assert.NoError(t, err) + assert.Equal(t, 10000, j1.Priority) + }) + t.Run("returns priority for leaf based on upstream", func(t *testing.T) { + upstream1 := &scheduler.JobUpstream{ + JobName: "upstream1", + Tenant: tnnt1, + } + + upstream2 := &scheduler.JobUpstream{ + JobName: "upstream2", + Tenant: tnnt1, + } + + j1 := &scheduler.JobWithDetails{ + Name: scheduler.JobName("RootNode"), + Job: &scheduler.Job{Tenant: tnnt1}, + Upstreams: scheduler.Upstreams{ + UpstreamJobs: []*scheduler.JobUpstream{upstream1, upstream2}, + }, + } + + s1 := resolver.SimpleResolver{} + err := s1.Resolve(ctx, []*scheduler.JobWithDetails{j1}) + assert.NoError(t, err) + assert.Equal(t, 9980, j1.Priority) + }) +} diff --git a/core/scheduler/service/deployment_service.go b/core/scheduler/service/deployment_service.go index eb5b1b35ea..62ccc3676e 100644 --- a/core/scheduler/service/deployment_service.go +++ b/core/scheduler/service/deployment_service.go @@ -2,88 +2,51 @@ package service import ( "context" - "fmt" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" - "github.com/odpf/optimus/internal/telemetry" -) - -func setJobMetric(t tenant.Tenant, jobs []*scheduler.JobWithDetails) { - telemetry.NewGauge("total_number_of_job", map[string]string{ - "project": t.ProjectName().String(), - "namespace": t.NamespaceName().String(), - }).Set(float64(len(jobs))) - - // this can be greatly simplified using a db query - type counter struct { - Inferred int - Static int - } - externalUpstreamCountMap := map[string]*counter{} - for _, job := range jobs { - for _, upstream := range job.Upstreams.UpstreamJobs { - if upstream.External { - if _, ok := externalUpstreamCountMap[upstream.Host]; !ok { - externalUpstreamCountMap[upstream.Host] = &counter{} - } - if upstream.Type == scheduler.UpstreamTypeStatic { - externalUpstreamCountMap[upstream.Host].Static++ - } else { - externalUpstreamCountMap[upstream.Host].Inferred++ - } - } - } - } - - for externalUpstream, counter := range externalUpstreamCountMap { - if counter.Static > 0 { - telemetry.NewGauge("total_external_upstream_references", map[string]string{ - "project": t.ProjectName().String(), - "namespace": t.NamespaceName().String(), - "host": externalUpstream, - "type": scheduler.UpstreamTypeStatic, - }).Set(float64(counter.Static)) - } + "go.opentelemetry.io/otel" - if counter.Inferred > 0 { - telemetry.NewGauge("total_external_upstream_references", map[string]string{ - "project": t.ProjectName().String(), - "namespace": t.NamespaceName().String(), - "host": externalUpstream, - "type": scheduler.UpstreamTypeInferred, - }).Set(float64(counter.Inferred)) - } - } -} + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" +) func (s *JobRunService) UploadToScheduler(ctx context.Context, projectName tenant.ProjectName) error { - multiError := errors.NewMultiError("errorInUploadToScheduler") - allJobsWithDetails, err := s.jobRepo.GetAll(ctx, projectName) - multiError.Append(err) + spanCtx, span := otel.Tracer("optimus").Start(ctx, "UploadToScheduler") + defer span.End() + + me := errors.NewMultiError("errorInUploadToScheduler") + allJobsWithDetails, err := s.jobRepo.GetAll(spanCtx, projectName) + me.Append(err) if allJobsWithDetails == nil { - return errors.MultiToError(multiError) + return me.ToErr() } - err = s.priorityResolver.Resolve(ctx, allJobsWithDetails) + span.AddEvent("got all the jobs to upload") + + err = s.priorityResolver.Resolve(spanCtx, allJobsWithDetails) if err != nil { - multiError.Append(err) - return errors.MultiToError(multiError) + s.l.Error("error resolving priority: %s", err) + me.Append(err) + return me.ToErr() } + span.AddEvent("done with priority resolution") + jobGroupByTenant := scheduler.GroupJobsByTenant(allJobsWithDetails) for t, jobs := range jobGroupByTenant { - if err = s.deployJobsPerNamespace(ctx, t, jobs); err == nil { - s.l.Debug(fmt.Sprintf("[success] namespace: %s, project: %s, deployed", t.NamespaceName().String(), t.ProjectName().String())) + span.AddEvent("uploading job specs") + if err = s.deployJobsPerNamespace(spanCtx, t, jobs); err == nil { + s.l.Info("[success] namespace: %s, project: %s, deployed", t.NamespaceName().String(), t.ProjectName().String()) } - multiError.Append(err) - setJobMetric(t, jobs) + me.Append(err) + + span.AddEvent("uploading job metrics") } - return multiError.ToErr() + return me.ToErr() } func (s *JobRunService) deployJobsPerNamespace(ctx context.Context, t tenant.Tenant, jobs []*scheduler.JobWithDetails) error { err := s.scheduler.DeployJobs(ctx, t, jobs) if err != nil { + s.l.Error("error deploying jobs under project [%s] namespace [%s]: %s", t.ProjectName().String(), t.NamespaceName().String(), err) return err } return s.cleanPerNamespace(ctx, t, jobs) @@ -93,6 +56,7 @@ func (s *JobRunService) cleanPerNamespace(ctx context.Context, t tenant.Tenant, // get all stored job names schedulerJobNames, err := s.scheduler.ListJobs(ctx, t) if err != nil { + s.l.Error("error listing jobs under project [%s] namespace [%s]: %s", t.ProjectName().String(), t.NamespaceName().String(), err) return err } jobNamesMap := make(map[string]struct{}) @@ -108,3 +72,38 @@ func (s *JobRunService) cleanPerNamespace(ctx context.Context, t tenant.Tenant, } return s.scheduler.DeleteJobs(ctx, t, jobsToDelete) } + +func (s *JobRunService) UploadJobs(ctx context.Context, tnnt tenant.Tenant, toUpdate, toDelete []string) (err error) { + me := errors.NewMultiError("errorInUploadJobs") + + if len(toUpdate) > 0 { + if err = s.resolveAndDeployJobs(ctx, tnnt, toUpdate); err == nil { + s.l.Info("[success] namespace: %s, project: %s, deployed %d jobs", tnnt.NamespaceName().String(), + tnnt.ProjectName().String(), len(toUpdate)) + } + me.Append(err) + } + + if len(toDelete) > 0 { + if err = s.scheduler.DeleteJobs(ctx, tnnt, toDelete); err == nil { + s.l.Info("deleted %s jobs on project: %s", len(toDelete), tnnt.ProjectName()) + } + me.Append(err) + } + + return me.ToErr() +} + +func (s *JobRunService) resolveAndDeployJobs(ctx context.Context, tnnt tenant.Tenant, toUpdate []string) error { + allJobsWithDetails, err := s.jobRepo.GetJobs(ctx, tnnt.ProjectName(), toUpdate) + if err != nil || allJobsWithDetails == nil { + return err + } + + if err := s.priorityResolver.Resolve(ctx, allJobsWithDetails); err != nil { + s.l.Error("error priority resolving jobs: %s", err) + return err + } + + return s.scheduler.DeployJobs(ctx, tnnt, allJobsWithDetails) +} diff --git a/core/scheduler/service/deployment_service_test.go b/core/scheduler/service/deployment_service_test.go index 300a40aef0..0bbd0d8fc7 100644 --- a/core/scheduler/service/deployment_service_test.go +++ b/core/scheduler/service/deployment_service_test.go @@ -2,16 +2,17 @@ package service_test import ( "context" + "errors" "fmt" "testing" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/scheduler/service" - "github.com/odpf/optimus/core/tenant" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/scheduler/service" + "github.com/raystack/optimus/core/tenant" ) func TestDeploymentService(t *testing.T) { @@ -73,11 +74,11 @@ func TestDeploymentService(t *testing.T) { t.Run("UploadToScheduler", func(t *testing.T) { t.Run("should return error if unable to get all jobs from job repo", func(t *testing.T) { jobRepo := new(JobRepository) - jobRepo.On("GetAll", ctx, proj1Name).Return(nil, fmt.Errorf("GetAll error")) + jobRepo.On("GetAll", mock.Anything, proj1Name).Return(nil, fmt.Errorf("GetAll error")) defer jobRepo.AssertExpectations(t) - runService := service.NewJobRunService(nil, - jobRepo, nil, nil, nil, nil, nil, nil) + runService := service.NewJobRunService(logger, + jobRepo, nil, nil, nil, nil, nil, nil, nil) err := runService.UploadToScheduler(ctx, proj1Name) assert.NotNil(t, err) @@ -85,15 +86,15 @@ func TestDeploymentService(t *testing.T) { }) t.Run("should return error if error in priority resolution", func(t *testing.T) { jobRepo := new(JobRepository) - jobRepo.On("GetAll", ctx, proj1Name).Return(jobsWithDetails, nil) + jobRepo.On("GetAll", mock.Anything, proj1Name).Return(jobsWithDetails, nil) defer jobRepo.AssertExpectations(t) priorityResolver := new(mockPriorityResolver) - priorityResolver.On("Resolve", ctx, jobsWithDetails).Return(fmt.Errorf("priority resolution error")) + priorityResolver.On("Resolve", mock.Anything, jobsWithDetails).Return(fmt.Errorf("priority resolution error")) defer priorityResolver.AssertExpectations(t) - runService := service.NewJobRunService(nil, - jobRepo, nil, nil, nil, nil, priorityResolver, nil) + runService := service.NewJobRunService(logger, + jobRepo, nil, nil, nil, nil, priorityResolver, nil, nil) err := runService.UploadToScheduler(ctx, proj1Name) assert.NotNil(t, err) @@ -101,20 +102,20 @@ func TestDeploymentService(t *testing.T) { }) t.Run("should deploy Jobs Per Namespace returning error", func(t *testing.T) { jobRepo := new(JobRepository) - jobRepo.On("GetAll", ctx, proj1Name).Return([]*scheduler.JobWithDetails{jobsWithDetails[0], jobsWithDetails[2]}, nil) + jobRepo.On("GetAll", mock.Anything, proj1Name).Return([]*scheduler.JobWithDetails{jobsWithDetails[0], jobsWithDetails[2]}, nil) defer jobRepo.AssertExpectations(t) priorityResolver := new(mockPriorityResolver) - priorityResolver.On("Resolve", ctx, []*scheduler.JobWithDetails{jobsWithDetails[0], jobsWithDetails[2]}).Return(nil) + priorityResolver.On("Resolve", mock.Anything, []*scheduler.JobWithDetails{jobsWithDetails[0], jobsWithDetails[2]}).Return(nil) defer priorityResolver.AssertExpectations(t) mScheduler := new(mockScheduler) - mScheduler.On("DeployJobs", ctx, tnnt1, []*scheduler.JobWithDetails{jobsWithDetails[0], jobsWithDetails[2]}). + mScheduler.On("DeployJobs", mock.Anything, tnnt1, []*scheduler.JobWithDetails{jobsWithDetails[0], jobsWithDetails[2]}). Return(fmt.Errorf("DeployJobs tnnt1 error")) defer mScheduler.AssertExpectations(t) runService := service.NewJobRunService(logger, jobRepo, nil, nil, nil, - mScheduler, priorityResolver, nil) + mScheduler, priorityResolver, nil, nil) err := runService.UploadToScheduler(ctx, proj1Name) assert.NotNil(t, err) @@ -122,58 +123,201 @@ func TestDeploymentService(t *testing.T) { }) t.Run("should deploy Jobs Per Namespace and cleanPerNamespace, appropriately", func(t *testing.T) { jobRepo := new(JobRepository) - jobRepo.On("GetAll", ctx, proj1Name).Return(jobsWithDetails, nil) + jobRepo.On("GetAll", mock.Anything, proj1Name).Return(jobsWithDetails, nil) defer jobRepo.AssertExpectations(t) priorityResolver := new(mockPriorityResolver) - priorityResolver.On("Resolve", ctx, jobsWithDetails).Return(nil) + priorityResolver.On("Resolve", mock.Anything, jobsWithDetails).Return(nil) defer priorityResolver.AssertExpectations(t) mScheduler := new(mockScheduler) - mScheduler.On("DeployJobs", ctx, tnnt1, []*scheduler.JobWithDetails{jobsWithDetails[0], jobsWithDetails[2]}). + mScheduler.On("DeployJobs", mock.Anything, tnnt1, []*scheduler.JobWithDetails{jobsWithDetails[0], jobsWithDetails[2]}). Return(nil) - mScheduler.On("DeployJobs", ctx, tnnt2, []*scheduler.JobWithDetails{jobsWithDetails[1]}). + mScheduler.On("DeployJobs", mock.Anything, tnnt2, []*scheduler.JobWithDetails{jobsWithDetails[1]}). Return(nil) - mScheduler.On("ListJobs", ctx, tnnt1).Return([]string{"job1", "job3"}, nil) - mScheduler.On("ListJobs", ctx, tnnt2).Return([]string{"job2", "job4-to-delete"}, nil) + mScheduler.On("ListJobs", mock.Anything, tnnt1).Return([]string{"job1", "job3"}, nil) + mScheduler.On("ListJobs", mock.Anything, tnnt2).Return([]string{"job2", "job4-to-delete"}, nil) var jobsToDelete []string - mScheduler.On("DeleteJobs", ctx, tnnt1, jobsToDelete).Return(nil) - mScheduler.On("DeleteJobs", ctx, tnnt2, []string{"job4-to-delete"}).Return(nil) + mScheduler.On("DeleteJobs", mock.Anything, tnnt1, jobsToDelete).Return(nil) + mScheduler.On("DeleteJobs", mock.Anything, tnnt2, []string{"job4-to-delete"}).Return(nil) defer mScheduler.AssertExpectations(t) runService := service.NewJobRunService(logger, jobRepo, nil, nil, nil, - mScheduler, priorityResolver, nil) + mScheduler, priorityResolver, nil, nil) err := runService.UploadToScheduler(ctx, proj1Name) assert.Nil(t, err) }) t.Run("should deploy Jobs Per Namespace and cleanPerNamespace, appropriately", func(t *testing.T) { jobRepo := new(JobRepository) - jobRepo.On("GetAll", ctx, proj1Name).Return(jobsWithDetails, nil) + jobRepo.On("GetAll", mock.Anything, proj1Name).Return(jobsWithDetails, nil) defer jobRepo.AssertExpectations(t) priorityResolver := new(mockPriorityResolver) - priorityResolver.On("Resolve", ctx, jobsWithDetails).Return(nil) + priorityResolver.On("Resolve", mock.Anything, jobsWithDetails).Return(nil) defer priorityResolver.AssertExpectations(t) mScheduler := new(mockScheduler) - mScheduler.On("DeployJobs", ctx, tnnt1, []*scheduler.JobWithDetails{jobsWithDetails[0], jobsWithDetails[2]}). + mScheduler.On("DeployJobs", mock.Anything, tnnt1, []*scheduler.JobWithDetails{jobsWithDetails[0], jobsWithDetails[2]}). Return(nil) - mScheduler.On("DeployJobs", ctx, tnnt2, []*scheduler.JobWithDetails{jobsWithDetails[1]}). + mScheduler.On("DeployJobs", mock.Anything, tnnt2, []*scheduler.JobWithDetails{jobsWithDetails[1]}). Return(nil) - mScheduler.On("ListJobs", ctx, tnnt1).Return([]string{}, fmt.Errorf("listJobs error")) - mScheduler.On("ListJobs", ctx, tnnt2).Return([]string{"job2", "job4-to-delete"}, nil) - mScheduler.On("DeleteJobs", ctx, tnnt2, []string{"job4-to-delete"}).Return(nil) + mScheduler.On("ListJobs", mock.Anything, tnnt1).Return([]string{}, fmt.Errorf("listJobs error")) + mScheduler.On("ListJobs", mock.Anything, tnnt2).Return([]string{"job2", "job4-to-delete"}, nil) + mScheduler.On("DeleteJobs", mock.Anything, tnnt2, []string{"job4-to-delete"}).Return(nil) defer mScheduler.AssertExpectations(t) runService := service.NewJobRunService(logger, jobRepo, nil, nil, nil, - mScheduler, priorityResolver, nil) + mScheduler, priorityResolver, nil, nil) err := runService.UploadToScheduler(ctx, proj1Name) assert.NotNil(t, err) assert.EqualError(t, err, "errorInUploadToScheduler:\n listJobs error") }) }) + + t.Run("UploadJobs", func(t *testing.T) { + t.Run("should return error if unable to get jobs", func(t *testing.T) { + jobNamesToUpload := []string{"job1", "job3"} + var jobNamesToDelete []string + + jobRepo := new(JobRepository) + jobRepo.On("GetJobs", mock.Anything, proj1Name, jobNamesToUpload).Return(nil, errors.New("internal error")) + defer jobRepo.AssertExpectations(t) + + priorityResolver := new(mockPriorityResolver) + defer priorityResolver.AssertExpectations(t) + + mScheduler := new(mockScheduler) + defer mScheduler.AssertExpectations(t) + + runService := service.NewJobRunService(logger, jobRepo, nil, nil, nil, + mScheduler, priorityResolver, nil, nil) + + err := runService.UploadJobs(ctx, tnnt1, jobNamesToUpload, jobNamesToDelete) + assert.Error(t, err) + }) + t.Run("should return error if unable to resolve priority", func(t *testing.T) { + jobNamesToUpload := []string{"job1", "job3"} + var jobNamesToDelete []string + jobsToUpload := []*scheduler.JobWithDetails{jobsWithDetails[0], jobsWithDetails[2]} + + jobRepo := new(JobRepository) + jobRepo.On("GetJobs", mock.Anything, proj1Name, jobNamesToUpload).Return(jobsToUpload, nil) + defer jobRepo.AssertExpectations(t) + + priorityResolver := new(mockPriorityResolver) + priorityResolver.On("Resolve", mock.Anything, jobsToUpload).Return(errors.New("internal error")) + defer priorityResolver.AssertExpectations(t) + + mScheduler := new(mockScheduler) + defer mScheduler.AssertExpectations(t) + + runService := service.NewJobRunService(logger, jobRepo, nil, nil, nil, + mScheduler, priorityResolver, nil, nil) + + err := runService.UploadJobs(ctx, tnnt1, jobNamesToUpload, jobNamesToDelete) + assert.Error(t, err) + }) + t.Run("should return error if unable to deploy jobs", func(t *testing.T) { + jobNamesToUpload := []string{"job1", "job3"} + var jobNamesToDelete []string + jobsToUpload := []*scheduler.JobWithDetails{jobsWithDetails[0], jobsWithDetails[2]} + + jobRepo := new(JobRepository) + jobRepo.On("GetJobs", mock.Anything, proj1Name, jobNamesToUpload).Return(jobsToUpload, nil) + defer jobRepo.AssertExpectations(t) + + priorityResolver := new(mockPriorityResolver) + priorityResolver.On("Resolve", mock.Anything, jobsToUpload).Return(nil) + defer priorityResolver.AssertExpectations(t) + + mScheduler := new(mockScheduler) + mScheduler.On("DeployJobs", mock.Anything, tnnt1, jobsToUpload).Return(errors.New("internal error")) + defer mScheduler.AssertExpectations(t) + + runService := service.NewJobRunService(logger, jobRepo, nil, nil, nil, + mScheduler, priorityResolver, nil, nil) + + err := runService.UploadJobs(ctx, tnnt1, jobNamesToUpload, jobNamesToDelete) + assert.Error(t, err) + }) + t.Run("should return error if unable to delete jobs from scheduler", func(t *testing.T) { + var jobNamesToUpload []string + jobNamesToDelete := []string{"job2"} + + mScheduler := new(mockScheduler) + mScheduler.On("DeleteJobs", mock.Anything, tnnt1, jobNamesToDelete).Return(errors.New("internal error")) + defer mScheduler.AssertExpectations(t) + + runService := service.NewJobRunService(logger, nil, nil, nil, nil, + mScheduler, nil, nil, nil) + + err := runService.UploadJobs(ctx, tnnt1, jobNamesToUpload, jobNamesToDelete) + assert.Error(t, err) + }) + t.Run("should upload and delete requested jobs, appropriately", func(t *testing.T) { + jobNamesToUpload := []string{"job1", "job3"} + jobNamesToDelete := []string{"job2"} + jobsToUpload := []*scheduler.JobWithDetails{jobsWithDetails[0], jobsWithDetails[2]} + + jobRepo := new(JobRepository) + jobRepo.On("GetJobs", mock.Anything, proj1Name, jobNamesToUpload).Return(jobsToUpload, nil) + defer jobRepo.AssertExpectations(t) + + priorityResolver := new(mockPriorityResolver) + priorityResolver.On("Resolve", mock.Anything, jobsToUpload).Return(nil) + defer priorityResolver.AssertExpectations(t) + + mScheduler := new(mockScheduler) + mScheduler.On("DeployJobs", mock.Anything, tnnt1, jobsToUpload).Return(nil) + mScheduler.On("DeleteJobs", mock.Anything, tnnt1, jobNamesToDelete).Return(nil) + defer mScheduler.AssertExpectations(t) + + runService := service.NewJobRunService(logger, jobRepo, nil, nil, nil, + mScheduler, priorityResolver, nil, nil) + + err := runService.UploadJobs(ctx, tnnt1, jobNamesToUpload, jobNamesToDelete) + assert.Nil(t, err) + }) + t.Run("should upload requested jobs, appropriately", func(t *testing.T) { + jobNamesToUpload := []string{"job1", "job3"} + var jobNamesToDelete []string + jobsToUpload := []*scheduler.JobWithDetails{jobsWithDetails[0], jobsWithDetails[2]} + + jobRepo := new(JobRepository) + jobRepo.On("GetJobs", mock.Anything, proj1Name, jobNamesToUpload).Return(jobsToUpload, nil) + defer jobRepo.AssertExpectations(t) + + priorityResolver := new(mockPriorityResolver) + priorityResolver.On("Resolve", mock.Anything, jobsToUpload).Return(nil) + defer priorityResolver.AssertExpectations(t) + + mScheduler := new(mockScheduler) + mScheduler.On("DeployJobs", mock.Anything, tnnt1, jobsToUpload).Return(nil) + defer mScheduler.AssertExpectations(t) + + runService := service.NewJobRunService(logger, jobRepo, nil, nil, nil, + mScheduler, priorityResolver, nil, nil) + + err := runService.UploadJobs(ctx, tnnt1, jobNamesToUpload, jobNamesToDelete) + assert.Nil(t, err) + }) + t.Run("should delete requested jobs, appropriately", func(t *testing.T) { + var jobNamesToUpload []string + jobNamesToDelete := []string{"job2"} + + mScheduler := new(mockScheduler) + mScheduler.On("DeleteJobs", mock.Anything, tnnt1, jobNamesToDelete).Return(nil) + defer mScheduler.AssertExpectations(t) + + runService := service.NewJobRunService(logger, nil, nil, nil, nil, + mScheduler, nil, nil, nil) + + err := runService.UploadJobs(ctx, tnnt1, jobNamesToUpload, jobNamesToDelete) + assert.Nil(t, err) + }) + }) } type mockPriorityResolver struct { diff --git a/core/scheduler/service/executor_input_compiler.go b/core/scheduler/service/executor_input_compiler.go index 2d7f455275..beea2a9075 100644 --- a/core/scheduler/service/executor_input_compiler.go +++ b/core/scheduler/service/executor_input_compiler.go @@ -5,10 +5,12 @@ import ( "strings" "time" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/compiler" - "github.com/odpf/optimus/internal/utils" + "github.com/raystack/salt/log" + + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/compiler" + "github.com/raystack/optimus/internal/utils" ) const ( @@ -53,16 +55,20 @@ type InputCompiler struct { tenantService TenantService compiler TemplateCompiler assetCompiler AssetCompiler + + logger log.Logger } func (i InputCompiler) Compile(ctx context.Context, job *scheduler.Job, config scheduler.RunConfig, executedAt time.Time) (*scheduler.ExecutorInput, error) { tenantDetails, err := i.tenantService.GetDetails(ctx, job.Tenant) if err != nil { + i.logger.Error("error getting tenant details: %s", err) return nil, err } systemDefinedVars, err := getSystemDefinedConfigs(job, config, executedAt) if err != nil { + i.logger.Error("error getting config for job [%s]: %s", job.Name.String(), err) return nil, err } @@ -76,11 +82,13 @@ func (i InputCompiler) Compile(ctx context.Context, job *scheduler.Job, config s // Compile asset files fileMap, err := i.assetCompiler.CompileJobRunAssets(ctx, job, systemDefinedVars, config.ScheduledAt, taskContext) if err != nil { + i.logger.Error("error compiling job run assets: %s", err) return nil, err } confs, secretConfs, err := i.compileConfigs(job.Task.Config, taskContext) if err != nil { + i.logger.Error("error compiling task config: %s", err) return nil, err } @@ -101,11 +109,13 @@ func (i InputCompiler) Compile(ctx context.Context, job *scheduler.Job, config s hook, err := job.GetHook(config.Executor.Name) if err != nil { + i.logger.Error("error getting hook [%s]: %s", config.Executor.Name, err) return nil, err } hookConfs, hookSecrets, err := i.compileConfigs(hook.Config, mergedContext) if err != nil { + i.logger.Error("error compiling configs for hook [%s]: %s", hook.Name, err) return nil, err } @@ -121,10 +131,12 @@ func (i InputCompiler) compileConfigs(configs map[string]string, templateCtx map var err error if conf, err = i.compiler.Compile(conf, templateCtx); err != nil { + i.logger.Error("error compiling template with config: %s", err) return nil, nil, err } if secretsConfig, err = i.compiler.Compile(secretsConfig, templateCtx); err != nil { + i.logger.Error("error compiling template with secret: %s", err) return nil, nil, err } @@ -162,10 +174,11 @@ func splitConfigWithSecrets(conf map[string]string) (map[string]string, map[stri return configs, configWithSecrets } -func NewJobInputCompiler(tenantService TenantService, compiler TemplateCompiler, assetCompiler AssetCompiler) *InputCompiler { +func NewJobInputCompiler(tenantService TenantService, compiler TemplateCompiler, assetCompiler AssetCompiler, logger log.Logger) *InputCompiler { return &InputCompiler{ tenantService: tenantService, compiler: compiler, assetCompiler: assetCompiler, + logger: logger, } } diff --git a/core/scheduler/service/executor_input_compiler_test.go b/core/scheduler/service/executor_input_compiler_test.go index 3c573d919d..ea4f775d6f 100644 --- a/core/scheduler/service/executor_input_compiler_test.go +++ b/core/scheduler/service/executor_input_compiler_test.go @@ -6,13 +6,14 @@ import ( "testing" "time" + "github.com/raystack/salt/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/scheduler/service" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/models" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/scheduler/service" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/models" ) func TestExecutorCompiler(t *testing.T) { @@ -34,6 +35,8 @@ func TestExecutorCompiler(t *testing.T) { currentTime := time.Now() scheduleTime := currentTime.Add(-time.Hour) + logger := log.NewLogrus() + t.Run("Compile", func(t *testing.T) { t.Run("should give error if tenant service getDetails fails", func(t *testing.T) { job := scheduler.Job{ @@ -53,7 +56,7 @@ func TestExecutorCompiler(t *testing.T) { tenantService.On("GetDetails", ctx, tnnt).Return(nil, fmt.Errorf("get details error")) defer tenantService.AssertExpectations(t) - inputCompiler := service.NewJobInputCompiler(tenantService, nil, nil) + inputCompiler := service.NewJobInputCompiler(tenantService, nil, nil, logger) inputExecutor, err := inputCompiler.Compile(ctx, &job, config, currentTime.Add(time.Hour)) assert.NotNil(t, err) @@ -80,7 +83,7 @@ func TestExecutorCompiler(t *testing.T) { tenantService.On("GetDetails", ctx, tnnt).Return(tenantDetails, nil) defer tenantService.AssertExpectations(t) - inputCompiler := service.NewJobInputCompiler(tenantService, nil, nil) + inputCompiler := service.NewJobInputCompiler(tenantService, nil, nil, logger) inputExecutor, err := inputCompiler.Compile(ctx, &job, config, currentTime.Add(time.Hour)) assert.NotNil(t, err) @@ -123,7 +126,7 @@ func TestExecutorCompiler(t *testing.T) { assetCompiler.On("CompileJobRunAssets", ctx, &job, systemDefinedVars, scheduleTime, taskContext).Return(nil, fmt.Errorf("CompileJobRunAssets error")) defer assetCompiler.AssertExpectations(t) - inputCompiler := service.NewJobInputCompiler(tenantService, nil, assetCompiler) + inputCompiler := service.NewJobInputCompiler(tenantService, nil, assetCompiler, logger) inputExecutor, err := inputCompiler.Compile(ctx, &job, config, executedAt) assert.NotNil(t, err) @@ -184,7 +187,7 @@ func TestExecutorCompiler(t *testing.T) { Return(nil, fmt.Errorf("some.config compilation error")) defer templateCompiler.AssertExpectations(t) - inputCompiler := service.NewJobInputCompiler(tenantService, templateCompiler, assetCompiler) + inputCompiler := service.NewJobInputCompiler(tenantService, templateCompiler, assetCompiler, logger) inputExecutor, err := inputCompiler.Compile(ctx, &job, config, executedAt) assert.NotNil(t, err) @@ -199,7 +202,7 @@ func TestExecutorCompiler(t *testing.T) { Return(nil, fmt.Errorf("secret.config compilation error")) defer templateCompiler.AssertExpectations(t) - inputCompiler := service.NewJobInputCompiler(tenantService, templateCompiler, assetCompiler) + inputCompiler := service.NewJobInputCompiler(tenantService, templateCompiler, assetCompiler, logger) inputExecutor, err := inputCompiler.Compile(ctx, &job, config, executedAt) assert.NotNil(t, err) @@ -214,7 +217,7 @@ func TestExecutorCompiler(t *testing.T) { Return(map[string]string{"secret.config.compiled": "a.secret.val.compiled"}, nil) defer templateCompiler.AssertExpectations(t) - inputCompiler := service.NewJobInputCompiler(tenantService, templateCompiler, assetCompiler) + inputCompiler := service.NewJobInputCompiler(tenantService, templateCompiler, assetCompiler, logger) inputExecutorResp, err := inputCompiler.Compile(ctx, &job, config, executedAt) assert.Nil(t, err) @@ -299,7 +302,7 @@ func TestExecutorCompiler(t *testing.T) { Return(map[string]string{"secret.hook.compiled": "hook.s.val.compiled"}, nil) defer templateCompiler.AssertExpectations(t) - inputCompiler := service.NewJobInputCompiler(tenantService, templateCompiler, assetCompiler) + inputCompiler := service.NewJobInputCompiler(tenantService, templateCompiler, assetCompiler, logger) inputExecutorResp, err := inputCompiler.Compile(ctx, &job, config, executedAt) assert.Nil(t, err) @@ -382,7 +385,7 @@ func TestExecutorCompiler(t *testing.T) { defer templateCompiler.AssertExpectations(t) - inputCompiler := service.NewJobInputCompiler(tenantService, templateCompiler, assetCompiler) + inputCompiler := service.NewJobInputCompiler(tenantService, templateCompiler, assetCompiler, logger) inputExecutorResp, err := inputCompiler.Compile(ctx, &job, config, executedAt) assert.NotNil(t, err) @@ -444,7 +447,7 @@ func TestExecutorCompiler(t *testing.T) { Return(map[string]string{"secret.config.compiled": "a.secret.val.compiled"}, nil) defer templateCompiler.AssertExpectations(t) - inputCompiler := service.NewJobInputCompiler(tenantService, templateCompiler, assetCompiler) + inputCompiler := service.NewJobInputCompiler(tenantService, templateCompiler, assetCompiler, logger) inputExecutorResp, err := inputCompiler.Compile(ctx, &job, config, executedAt) assert.NotNil(t, err) diff --git a/core/scheduler/service/job_run_asset_compiler.go b/core/scheduler/service/job_run_asset_compiler.go index da7c2aeb58..27e0613aba 100644 --- a/core/scheduler/service/job_run_asset_compiler.go +++ b/core/scheduler/service/job_run_asset_compiler.go @@ -5,8 +5,10 @@ import ( "fmt" "time" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/sdk/plugin" + "github.com/raystack/salt/log" + + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/sdk/plugin" ) const ( @@ -24,27 +26,33 @@ type PluginRepo interface { type JobRunAssetsCompiler struct { compiler FilesCompiler pluginRepo PluginRepo + + logger log.Logger } -func NewJobAssetsCompiler(engine FilesCompiler, pluginRepo PluginRepo) *JobRunAssetsCompiler { +func NewJobAssetsCompiler(engine FilesCompiler, pluginRepo PluginRepo, logger log.Logger) *JobRunAssetsCompiler { return &JobRunAssetsCompiler{ compiler: engine, pluginRepo: pluginRepo, + logger: logger, } } func (c *JobRunAssetsCompiler) CompileJobRunAssets(ctx context.Context, job *scheduler.Job, systemEnvVars map[string]string, scheduledAt time.Time, contextForTask map[string]interface{}) (map[string]string, error) { taskPlugin, err := c.pluginRepo.GetByName(job.Task.Name) if err != nil { + c.logger.Error("error getting plugin [%s]: %s", job.Task.Name, err) return nil, err } startTime, err := job.Window.GetStartTime(scheduledAt) if err != nil { + c.logger.Error("error getting window start time: %s", err) return nil, fmt.Errorf("error getting start time: %w", err) } endTime, err := job.Window.GetEndTime(scheduledAt) if err != nil { + c.logger.Error("error getting window end time: %s", err) return nil, fmt.Errorf("error getting end time: %w", err) } @@ -60,6 +68,7 @@ func (c *JobRunAssetsCompiler) CompileJobRunAssets(ctx context.Context, job *sch InstanceData: toJobRunSpecData(systemEnvVars), }) if err != nil { + c.logger.Error("error compiling assets through plugin dependency mod: %s", err) return nil, err } inputFiles = compiledAssetResponse.Assets.ToMap() @@ -67,6 +76,7 @@ func (c *JobRunAssetsCompiler) CompileJobRunAssets(ctx context.Context, job *sch fileMap, err := c.compiler.Compile(inputFiles, contextForTask) if err != nil { + c.logger.Error("error compiling assets: %s", err) return nil, err } return fileMap, nil diff --git a/core/scheduler/service/job_run_asset_compiler_test.go b/core/scheduler/service/job_run_asset_compiler_test.go index d2647a2c49..07f0264110 100644 --- a/core/scheduler/service/job_run_asset_compiler_test.go +++ b/core/scheduler/service/job_run_asset_compiler_test.go @@ -6,15 +6,16 @@ import ( "testing" "time" + "github.com/raystack/salt/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/scheduler/service" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/models" - "github.com/odpf/optimus/sdk/plugin" - smock "github.com/odpf/optimus/sdk/plugin/mock" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/scheduler/service" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/models" + "github.com/raystack/optimus/sdk/plugin" + smock "github.com/raystack/optimus/sdk/plugin/mock" ) func TestJobAssetsCompiler(t *testing.T) { @@ -54,6 +55,8 @@ func TestJobAssetsCompiler(t *testing.T) { "JOB_DESTINATION": job.Destination, } + logger := log.NewLogrus() + t.Run("CompileJobRunAssets", func(t *testing.T) { t.Run("should error if plugin repo get plugin by name fails", func(t *testing.T) { pluginRepo := new(mockPluginRepo) @@ -62,7 +65,7 @@ func TestJobAssetsCompiler(t *testing.T) { contextForTask := map[string]any{} - jobRunAssetsCompiler := service.NewJobAssetsCompiler(nil, pluginRepo) + jobRunAssetsCompiler := service.NewJobAssetsCompiler(nil, pluginRepo, logger) assets, err := jobRunAssetsCompiler.CompileJobRunAssets(ctx, job, systemEnvVars, scheduleTime, contextForTask) assert.NotNil(t, err) assert.EqualError(t, err, "error in getting plugin by name") @@ -90,7 +93,7 @@ func TestJobAssetsCompiler(t *testing.T) { }, } - jobRunAssetsCompiler := service.NewJobAssetsCompiler(nil, pluginRepo) + jobRunAssetsCompiler := service.NewJobAssetsCompiler(nil, pluginRepo, logger) contextForTask := map[string]any{} assets, err := jobRunAssetsCompiler.CompileJobRunAssets(ctx, job1, systemEnvVars, scheduleTime, contextForTask) @@ -111,7 +114,7 @@ func TestJobAssetsCompiler(t *testing.T) { YamlMod: yamlMod, }, nil) defer pluginRepo.AssertExpectations(t) - jobRunAssetsCompiler := service.NewJobAssetsCompiler(nil, pluginRepo) + jobRunAssetsCompiler := service.NewJobAssetsCompiler(nil, pluginRepo, logger) contextForTask := map[string]any{} assets, err := jobRunAssetsCompiler.CompileJobRunAssets(ctx, job, systemEnvVars, scheduleTime, contextForTask) @@ -150,7 +153,7 @@ func TestJobAssetsCompiler(t *testing.T) { Return(nil, fmt.Errorf("error in compiling")) defer filesCompiler.AssertExpectations(t) - jobRunAssetsCompiler := service.NewJobAssetsCompiler(filesCompiler, pluginRepo) + jobRunAssetsCompiler := service.NewJobAssetsCompiler(filesCompiler, pluginRepo, logger) assets, err := jobRunAssetsCompiler.CompileJobRunAssets(ctx, job, systemEnvVars, scheduleTime, contextForTask) assert.NotNil(t, err) @@ -167,7 +170,7 @@ func TestJobAssetsCompiler(t *testing.T) { Return(expectedFileMap, nil) defer filesCompiler.AssertExpectations(t) - jobRunAssetsCompiler := service.NewJobAssetsCompiler(filesCompiler, pluginRepo) + jobRunAssetsCompiler := service.NewJobAssetsCompiler(filesCompiler, pluginRepo, logger) assets, err := jobRunAssetsCompiler.CompileJobRunAssets(ctx, job, systemEnvVars, scheduleTime, contextForTask) assert.Nil(t, err) diff --git a/core/scheduler/service/job_run_service.go b/core/scheduler/service/job_run_service.go index 56771e8322..03b3cd4353 100644 --- a/core/scheduler/service/job_run_service.go +++ b/core/scheduler/service/job_run_service.go @@ -2,17 +2,21 @@ package service import ( "context" + "encoding/json" "fmt" + "strings" "time" "github.com/google/uuid" - "github.com/odpf/salt/log" - - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" - "github.com/odpf/optimus/internal/lib/cron" - "github.com/odpf/optimus/internal/telemetry" + "github.com/raystack/salt/log" + + "github.com/raystack/optimus/core/event" + "github.com/raystack/optimus/core/event/moderator" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/lib/cron" + "github.com/raystack/optimus/internal/telemetry" ) type metricType string @@ -23,12 +27,15 @@ func (m metricType) String() string { const ( scheduleDelay metricType = "schedule_delay" + + metricJobRunEvents = "jobrun_events_total" ) type JobRepository interface { GetJob(ctx context.Context, name tenant.ProjectName, jobName scheduler.JobName) (*scheduler.Job, error) GetJobDetails(ctx context.Context, projectName tenant.ProjectName, jobName scheduler.JobName) (*scheduler.JobWithDetails, error) GetAll(ctx context.Context, projectName tenant.ProjectName) ([]*scheduler.JobWithDetails, error) + GetJobs(ctx context.Context, projectName tenant.ProjectName, jobs []string) ([]*scheduler.JobWithDetails, error) } type JobRunRepository interface { @@ -36,6 +43,7 @@ type JobRunRepository interface { GetByScheduledAt(ctx context.Context, tenant tenant.Tenant, name scheduler.JobName, scheduledAt time.Time) (*scheduler.JobRun, error) Create(ctx context.Context, tenant tenant.Tenant, name scheduler.JobName, scheduledAt time.Time, slaDefinitionInSec int64) error Update(ctx context.Context, jobRunID uuid.UUID, endTime time.Time, jobRunStatus scheduler.State) error + UpdateState(ctx context.Context, jobRunID uuid.UUID, jobRunStatus scheduler.State) error UpdateSLA(ctx context.Context, slaObjects []*scheduler.SLAObject) error UpdateMonitoring(ctx context.Context, jobRunID uuid.UUID, monitoring map[string]any) error } @@ -65,11 +73,16 @@ type Scheduler interface { DeleteJobs(ctx context.Context, t tenant.Tenant, jobsToDelete []string) error } +type EventHandler interface { + HandleEvent(moderator.Event) +} + type JobRunService struct { l log.Logger repo JobRunRepository replayRepo JobReplayRepository operatorRunRepo OperatorRunRepository + eventHandler EventHandler scheduler Scheduler jobRepo JobRepository priorityResolver PriorityResolver @@ -79,25 +92,31 @@ type JobRunService struct { func (s *JobRunService) JobRunInput(ctx context.Context, projectName tenant.ProjectName, jobName scheduler.JobName, config scheduler.RunConfig) (*scheduler.ExecutorInput, error) { job, err := s.jobRepo.GetJob(ctx, projectName, jobName) if err != nil { + s.l.Error("error getting job [%s]: %s", jobName, err) return nil, err } // TODO: Use scheduled_at instead of executed_at for computations, for deterministic calculations // Todo: later, always return scheduleTime, for scheduleTimes greater than a given date var jobRun *scheduler.JobRun if config.JobRunID.IsEmpty() { + s.l.Warn("getting job run by scheduled at") jobRun, err = s.repo.GetByScheduledAt(ctx, job.Tenant, jobName, config.ScheduledAt) } else { + s.l.Warn("getting job run by id") jobRun, err = s.repo.GetByID(ctx, config.JobRunID) } + var executedAt time.Time if err != nil { // Fallback for executed_at to scheduled_at executedAt = config.ScheduledAt + s.l.Warn("suppressed error is encountered when getting job run: %s", err) } else { executedAt = jobRun.StartTime } // Additional task config from existing replay replayJobConfig, err := s.replayRepo.GetReplayJobConfig(ctx, job.Tenant, job.Name, config.ScheduledAt) if err != nil { + s.l.Error("error getting replay job config from db: %s", err) return nil, err } for k, v := range replayJobConfig { @@ -110,39 +129,40 @@ func (s *JobRunService) JobRunInput(ctx context.Context, projectName tenant.Proj func (s *JobRunService) GetJobRuns(ctx context.Context, projectName tenant.ProjectName, jobName scheduler.JobName, criteria *scheduler.JobRunsCriteria) ([]*scheduler.JobRunStatus, error) { jobWithDetails, err := s.jobRepo.GetJobDetails(ctx, projectName, jobName) if err != nil { - return nil, fmt.Errorf("unable to get job details from DB for jobName: %s, project:%s, error:%w ", jobName, projectName, err) + msg := fmt.Sprintf("unable to get job details for jobName: %s, project:%s", jobName, projectName) + s.l.Error(msg) + return nil, errors.AddErrContext(err, scheduler.EntityJobRun, msg) } interval := jobWithDetails.Schedule.Interval if interval == "" { - return nil, fmt.Errorf("job schedule interval not found") + s.l.Error("job schedule interval is empty") + return nil, errors.InvalidArgument(scheduler.EntityJobRun, "cannot get job runs, job interval is empty") } - // jobCron jobCron, err := cron.ParseCronSchedule(interval) if err != nil { - return nil, fmt.Errorf("unable to parse job cron interval %w", err) + msg := fmt.Sprintf("unable to parse job cron interval: %s", err) + s.l.Error(msg) + return nil, errors.InternalError(scheduler.EntityJobRun, msg, nil) } if criteria.OnlyLastRun { + s.l.Warn("getting last run only") return s.scheduler.GetJobRuns(ctx, jobWithDetails.Job.Tenant, criteria, jobCron) } - // validate job query err = validateJobQuery(criteria, jobWithDetails) if err != nil { + s.l.Error("invalid job query: %s", err) return nil, err } - // get expected runs StartDate and EndDate inclusive expectedRuns := getExpectedRuns(jobCron, criteria.StartDate, criteria.EndDate) - // call to airflow for get runs actualRuns, err := s.scheduler.GetJobRuns(ctx, jobWithDetails.Job.Tenant, criteria, jobCron) if err != nil { - s.l.Error(fmt.Sprintf("unable to get job runs from airflow err: %v", err.Error())) + s.l.Error("unable to get job runs from airflow err: %s", err) actualRuns = []*scheduler.JobRunStatus{} } - // mergeRuns totalRuns := mergeRuns(expectedRuns, actualRuns) - // filterRuns result := filterRuns(totalRuns, createFilterSet(criteria.Filter)) return result, nil @@ -164,16 +184,16 @@ func getExpectedRuns(spec *cron.ScheduleSpec, startTime, endTime time.Time) []*s } func mergeRuns(expected, actual []*scheduler.JobRunStatus) []*scheduler.JobRunStatus { - var mergeRuns []*scheduler.JobRunStatus + var merged []*scheduler.JobRunStatus m := actualRunMap(actual) for _, exp := range expected { if act, ok := m[exp.ScheduledAt.UTC().String()]; ok { - mergeRuns = append(mergeRuns, &act) + merged = append(merged, &act) } else { - mergeRuns = append(mergeRuns, exp) + merged = append(merged, exp) } } - return mergeRuns + return merged } func actualRunMap(runs []*scheduler.JobRunStatus) map[string]scheduler.JobRunStatus { @@ -208,13 +228,10 @@ func createFilterSet(filter []string) map[string]struct{} { func validateJobQuery(jobQuery *scheduler.JobRunsCriteria, jobWithDetails *scheduler.JobWithDetails) error { jobStartDate := jobWithDetails.Schedule.StartDate if jobStartDate.IsZero() { - return fmt.Errorf("job schedule startDate not found in job fetched from DB") + return errors.InternalError(scheduler.EntityJobRun, "job schedule startDate not found in job", nil) } - givenStartDate := jobQuery.StartDate - givenEndDate := jobQuery.EndDate - - if givenStartDate.Before(jobStartDate) || givenEndDate.Before(jobStartDate) { - return fmt.Errorf("invalid date range") + if jobQuery.StartDate.Before(jobStartDate) || jobQuery.EndDate.Before(jobStartDate) { + return errors.InvalidArgument(scheduler.EntityJobRun, "invalid date range, interval contains dates before job start") } return nil } @@ -222,26 +239,26 @@ func validateJobQuery(jobQuery *scheduler.JobRunsCriteria, jobWithDetails *sched func (s *JobRunService) registerNewJobRun(ctx context.Context, tenant tenant.Tenant, jobName scheduler.JobName, scheduledAt time.Time) error { job, err := s.jobRepo.GetJobDetails(ctx, tenant.ProjectName(), jobName) if err != nil { + s.l.Error("error getting job details for job [%s]: %s", jobName, err) return err } slaDefinitionInSec, err := job.SLADuration() if err != nil { + s.l.Error("error getting sla duration: %s", err) return err } - telemetry.NewGauge("total_jobs_running", map[string]string{ - "project": tenant.ProjectName().String(), - "namespace": tenant.NamespaceName().String(), - }).Inc() err = s.repo.Create(ctx, tenant, jobName, scheduledAt, slaDefinitionInSec) if err != nil { + s.l.Error("error creating job run: %s", err) return err } - telemetry.NewCounter("scheduler_operator_durations_seconds", map[string]string{ + telemetry.NewGauge("jobrun_durations_breakdown_seconds", map[string]string{ "project": tenant.ProjectName().String(), "namespace": tenant.NamespaceName().String(), + "job": jobName.String(), "type": scheduleDelay.String(), - }).Add(float64(time.Now().Unix() - scheduledAt.Unix())) + }).Set(float64(time.Now().Unix() - scheduledAt.Unix())) return nil } @@ -250,14 +267,18 @@ func (s *JobRunService) getJobRunByScheduledAt(ctx context.Context, tenant tenan jobRun, err := s.repo.GetByScheduledAt(ctx, tenant, jobName, scheduledAt) if err != nil { if !errors.IsErrorType(err, errors.ErrNotFound) { + s.l.Error("error getting job run by scheduled at: %s", err) return nil, err } + // TODO: consider moving below call outside as the caller is a 'getter' err = s.registerNewJobRun(ctx, tenant, jobName, scheduledAt) if err != nil { + s.l.Error("error registering new job run: %s", err) return nil, err } jobRun, err = s.repo.GetByScheduledAt(ctx, tenant, jobName, scheduledAt) if err != nil { + s.l.Error("error getting the registered job run: %s", err) return nil, err } } @@ -268,23 +289,15 @@ func (s *JobRunService) updateJobRun(ctx context.Context, event *scheduler.Event var jobRun *scheduler.JobRun jobRun, err := s.getJobRunByScheduledAt(ctx, event.Tenant, event.JobName, event.JobScheduledAt) if err != nil { + s.l.Error("error getting job run by schedule time [%s]: %s", event.JobScheduledAt, err) return err } - for _, state := range scheduler.TaskEndStates { - if event.Status == state { - // this can go negative, because it is possible that when we deploy certain job have already started, - // and the very first events we get are that of task end states, to handle this, we should treat the lowest - // value as the base value. - telemetry.NewGauge("total_jobs_running", map[string]string{ - "project": event.Tenant.ProjectName().String(), - "namespace": event.Tenant.NamespaceName().String(), - }).Dec() - break - } - } if err := s.repo.Update(ctx, jobRun.ID, event.EventTime, event.Status); err != nil { + s.l.Error("error updating job run with id [%s]: %s", jobRun.ID, err) return err } + jobRun.State = event.Status + s.raiseJobRunStateChangeEvent(jobRun) monitoringValues := s.getMonitoringValues(event) return s.repo.UpdateMonitoring(ctx, jobRun.ID, monitoringValues) } @@ -298,24 +311,77 @@ func (*JobRunService) getMonitoringValues(event *scheduler.Event) map[string]any } func (s *JobRunService) updateJobRunSLA(ctx context.Context, event *scheduler.Event) error { + telemetry.NewCounter(metricJobRunEvents, map[string]string{ + "project": event.Tenant.ProjectName().String(), + "namespace": event.Tenant.NamespaceName().String(), + "name": event.JobName.String(), + "status": scheduler.SLAMissEvent.String(), + }).Inc() if len(event.SLAObjectList) > 0 { return s.repo.UpdateSLA(ctx, event.SLAObjectList) } return nil } +func operatorStartToJobState(operatorType scheduler.OperatorType) (scheduler.State, error) { + switch operatorType { + case scheduler.OperatorTask: + return scheduler.StateInProgress, nil + case scheduler.OperatorSensor: + return scheduler.StateWaitUpstream, nil + case scheduler.OperatorHook: + return scheduler.StateInProgress, nil + default: + return "", errors.InvalidArgument(scheduler.EntityJobRun, "Invalid operator type") + } +} + +func (s *JobRunService) raiseJobRunStateChangeEvent(jobRun *scheduler.JobRun) { + var schedulerEvent moderator.Event + var err error + switch jobRun.State { + case scheduler.StateWaitUpstream: + schedulerEvent, err = event.NewJobRunWaitUpstreamEvent(jobRun) + case scheduler.StateInProgress: + schedulerEvent, err = event.NewJobRunInProgressEvent(jobRun) + case scheduler.StateSuccess: + schedulerEvent, err = event.NewJobRunSuccessEvent(jobRun) + case scheduler.StateFailed: + schedulerEvent, err = event.NewJobRunFailedEvent(jobRun) + } + if err != nil { + s.l.Error("error creating event for job run state change : %s", err) + return + } + s.eventHandler.HandleEvent(schedulerEvent) + telemetry.NewCounter(metricJobRunEvents, map[string]string{ + "project": jobRun.Tenant.ProjectName().String(), + "namespace": jobRun.Tenant.NamespaceName().String(), + "name": jobRun.JobName.String(), + "status": jobRun.State.String(), + }).Inc() +} + func (s *JobRunService) createOperatorRun(ctx context.Context, event *scheduler.Event, operatorType scheduler.OperatorType) error { jobRun, err := s.getJobRunByScheduledAt(ctx, event.Tenant, event.JobName, event.JobScheduledAt) if err != nil { + s.l.Error("error getting job run by scheduled time [%s]: %s", event.JobScheduledAt, err) return err } - if operatorType == scheduler.OperatorTask { - telemetry.NewGauge("count_running_tasks", map[string]string{ - "project": event.Tenant.ProjectName().String(), - "namespace": event.Tenant.NamespaceName().String(), - "type": event.OperatorName, - }).Inc() + jobState, err := operatorStartToJobState(operatorType) + if err != nil { + s.l.Error("error converting operator to job state: %s", err) + return err } + if jobRun.State != jobState { + err := s.repo.UpdateState(ctx, jobRun.ID, jobState) + if err != nil { + s.l.Error("error updating state for job run id [%d] to [%s]: %s", jobRun.ID, jobState, err) + return err + } + s.raiseJobRunStateChangeEvent(jobRun) + } + return s.operatorRunRepo.CreateOperatorRun(ctx, event.OperatorName, operatorType, jobRun.ID, event.EventTime) } @@ -324,14 +390,20 @@ func (s *JobRunService) getOperatorRun(ctx context.Context, event *scheduler.Eve operatorRun, err := s.operatorRunRepo.GetOperatorRun(ctx, event.OperatorName, operatorType, jobRunID) if err != nil { if !errors.IsErrorType(err, errors.ErrNotFound) { + s.l.Error("error getting operator for job run [%s]: %s", jobRunID, err) return nil, err } + s.l.Warn("operator is not found, creating it") + + // TODO: consider moving below call outside as the caller is a 'getter' err = s.createOperatorRun(ctx, event, operatorType) if err != nil { + s.l.Error("error creating operator run: %s", err) return nil, err } operatorRun, err = s.operatorRunRepo.GetOperatorRun(ctx, event.OperatorName, operatorType, jobRunID) if err != nil { + s.l.Error("error getting the registered operator run: %s", err) return nil, err } } @@ -341,52 +413,68 @@ func (s *JobRunService) getOperatorRun(ctx context.Context, event *scheduler.Eve func (s *JobRunService) updateOperatorRun(ctx context.Context, event *scheduler.Event, operatorType scheduler.OperatorType) error { jobRun, err := s.getJobRunByScheduledAt(ctx, event.Tenant, event.JobName, event.JobScheduledAt) if err != nil { + s.l.Error("error getting job run by scheduled time [%s]: %s", event.JobScheduledAt, err) return err } operatorRun, err := s.getOperatorRun(ctx, event, operatorType, jobRun.ID) if err != nil { + s.l.Error("error getting operator for job run id [%s]: %s", jobRun.ID, err) return err } - if operatorType == scheduler.OperatorTask { - for _, state := range scheduler.TaskEndStates { - if event.Status == state { - // this can go negative, because it is possible that when we deploy certain job have already started, - // and the very first events we get are that of task end states, to handle this, we should treat the lowest - // value as the base value. - telemetry.NewGauge("count_running_tasks", map[string]string{ - "project": event.Tenant.ProjectName().String(), - "namespace": event.Tenant.NamespaceName().String(), - "type": event.OperatorName, - }).Dec() - break - } - } - } err = s.operatorRunRepo.UpdateOperatorRun(ctx, operatorType, operatorRun.ID, event.EventTime, event.Status) if err != nil { + s.l.Error("error updating operator run id [%s]: %s", operatorRun.ID, err) return err } - telemetry.NewCounter("scheduler_operator_durations_seconds", map[string]string{ + telemetry.NewGauge("jobrun_durations_breakdown_seconds", map[string]string{ "project": event.Tenant.ProjectName().String(), "namespace": event.Tenant.NamespaceName().String(), + "job": event.JobName.String(), "type": operatorType.String(), - }).Add(float64(event.EventTime.Unix() - operatorRun.StartTime.Unix())) + }).Set(float64(event.EventTime.Unix() - operatorRun.StartTime.Unix())) return nil } func (s *JobRunService) trackEvent(event *scheduler.Event) { if event.Type.IsOfType(scheduler.EventCategorySLAMiss) { - s.l.Debug(fmt.Sprintf("received event: %v, jobName: %v , slaPayload: %#v", - event.Type, event.JobName, event.SLAObjectList)) + jsonSLAObjectList, err := json.Marshal(event.SLAObjectList) + if err != nil { + jsonSLAObjectList = []byte("unable to json Marshal SLAObjectList") + } + s.l.Info("received job sla_miss event, jobName: %v , slaPayload: %s", event.JobName, string(jsonSLAObjectList)) } else { - s.l.Debug(fmt.Sprintf("received event: %v, eventTime: %s, jobName: %v, Operator: %v, schedule: %s, status: %s", - event.Type, event.EventTime.Format("01/02/06 15:04:05 MST"), event.JobName, event.OperatorName, event.JobScheduledAt.Format("01/02/06 15:04:05 MST"), event.Status)) + s.l.Info("received event: %v, eventTime: %s, jobName: %v, Operator: %v, schedule: %s, status: %s", + event.Type, event.EventTime.Format("01/02/06 15:04:05 MST"), event.JobName, event.OperatorName, event.JobScheduledAt.Format("01/02/06 15:04:05 MST"), event.Status) + } + + if event.Type == scheduler.SensorStartEvent || event.Type == scheduler.SensorRetryEvent || event.Type == scheduler.SensorSuccessEvent || event.Type == scheduler.SensorFailEvent { + eventType := strings.TrimPrefix(event.Type.String(), fmt.Sprintf("%s_", scheduler.OperatorSensor)) + telemetry.NewCounter("jobrun_sensor_events_total", map[string]string{ + "project": event.Tenant.ProjectName().String(), + "namespace": event.Tenant.NamespaceName().String(), + "event_type": eventType, + }).Inc() + return + } + if event.Type == scheduler.TaskStartEvent || event.Type == scheduler.TaskRetryEvent || event.Type == scheduler.TaskSuccessEvent || event.Type == scheduler.TaskFailEvent { + eventType := strings.TrimPrefix(event.Type.String(), fmt.Sprintf("%s_", scheduler.OperatorTask)) + telemetry.NewCounter("jobrun_task_events_total", map[string]string{ + "project": event.Tenant.ProjectName().String(), + "namespace": event.Tenant.NamespaceName().String(), + "event_type": eventType, + "operator": event.OperatorName, + }).Inc() + return + } + if event.Type == scheduler.HookStartEvent || event.Type == scheduler.HookRetryEvent || event.Type == scheduler.HookSuccessEvent || event.Type == scheduler.HookFailEvent { + eventType := strings.TrimPrefix(event.Type.String(), fmt.Sprintf("%s_", scheduler.OperatorHook)) + telemetry.NewCounter("jobrun_hook_events_total", map[string]string{ + "project": event.Tenant.ProjectName().String(), + "namespace": event.Tenant.NamespaceName().String(), + "event_type": eventType, + "operator": event.OperatorName, + }).Inc() } - telemetry.NewGauge("scheduler_events", map[string]string{ - "project": event.Tenant.ProjectName().String(), - "namespace": event.Tenant.NamespaceName().String(), - "type": event.Type.String(), - }).Inc() } func (s *JobRunService) UpdateJobState(ctx context.Context, event *scheduler.Event) error { @@ -415,13 +503,14 @@ func (s *JobRunService) UpdateJobState(ctx context.Context, event *scheduler.Eve } func NewJobRunService(logger log.Logger, jobRepo JobRepository, jobRunRepo JobRunRepository, replayRepo JobReplayRepository, - operatorRunRepo OperatorRunRepository, scheduler Scheduler, resolver PriorityResolver, compiler JobInputCompiler, + operatorRunRepo OperatorRunRepository, scheduler Scheduler, resolver PriorityResolver, compiler JobInputCompiler, eventHandler EventHandler, ) *JobRunService { return &JobRunService{ l: logger, repo: jobRunRepo, operatorRunRepo: operatorRunRepo, scheduler: scheduler, + eventHandler: eventHandler, replayRepo: replayRepo, jobRepo: jobRepo, priorityResolver: resolver, diff --git a/core/scheduler/service/job_run_service_test.go b/core/scheduler/service/job_run_service_test.go index e78dbf1b80..167e159c79 100644 --- a/core/scheduler/service/job_run_service_test.go +++ b/core/scheduler/service/job_run_service_test.go @@ -7,15 +7,16 @@ import ( "time" "github.com/google/uuid" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/scheduler/service" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" - "github.com/odpf/optimus/internal/lib/cron" + "github.com/raystack/optimus/core/event/moderator" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/scheduler/service" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/lib/cron" ) func TestJobRunService(t *testing.T) { @@ -37,7 +38,7 @@ func TestJobRunService(t *testing.T) { t.Run("should reject unregistered events", func(t *testing.T) { runService := service.NewJobRunService(logger, - nil, nil, nil, nil, nil, nil, nil) + nil, nil, nil, nil, nil, nil, nil, nil) event := &scheduler.Event{ JobName: jobName, @@ -60,7 +61,7 @@ func TestJobRunService(t *testing.T) { defer jobRepo.AssertExpectations(t) runService := service.NewJobRunService(logger, - jobRepo, jobRunRepository, nil, nil, nil, nil, nil) + jobRepo, jobRunRepository, nil, nil, nil, nil, nil, nil) event := &scheduler.Event{ JobName: jobName, @@ -105,7 +106,7 @@ func TestJobRunService(t *testing.T) { defer jobRunRepository.AssertExpectations(t) runService := service.NewJobRunService(logger, - jobRepo, jobRunRepository, nil, nil, nil, nil, nil) + jobRepo, jobRunRepository, nil, nil, nil, nil, nil, nil) event := &scheduler.Event{ JobName: jobName, @@ -179,8 +180,12 @@ func TestJobRunService(t *testing.T) { operatorRunRepo := new(mockOperatorRunRepository) defer operatorRunRepo.AssertExpectations(t) + eventHandler := newEventHandler(t) + eventHandler.On("HandleEvent", mock.Anything).Times(1) + defer eventHandler.AssertExpectations(t) + runService := service.NewJobRunService(logger, - jobRepo, jobRunRepo, nil, operatorRunRepo, nil, nil, nil) + jobRepo, jobRunRepo, nil, operatorRunRepo, nil, nil, nil, eventHandler) err = runService.UpdateJobState(ctx, event) assert.Nil(t, err) @@ -218,8 +223,12 @@ func TestJobRunService(t *testing.T) { jobRunRepo.On("UpdateMonitoring", ctx, jobRun.ID, monitoring).Return(nil) defer jobRunRepo.AssertExpectations(t) + eventHandler := newEventHandler(t) + eventHandler.On("HandleEvent", mock.Anything).Times(1) + defer eventHandler.AssertExpectations(t) + runService := service.NewJobRunService(logger, - nil, jobRunRepo, nil, nil, nil, nil, nil) + nil, jobRunRepo, nil, nil, nil, nil, nil, eventHandler) err := runService.UpdateJobState(ctx, event) assert.Nil(t, err) @@ -245,9 +254,6 @@ func TestJobRunService(t *testing.T) { }, }, } - jobRepo := new(JobRepository) - jobRepo.On("GetJobDetails", ctx, projName, jobName).Return(&jobWithDetails, nil) - defer jobRepo.AssertExpectations(t) scheduledAtTimeStamp, _ := time.Parse(scheduler.ISODateFormat, "2022-01-02T15:04:05Z") eventTime := time.Unix(todayDate.Add(time.Hour).Unix(), 0) @@ -279,7 +285,7 @@ func TestJobRunService(t *testing.T) { defer jobRunRepo.AssertExpectations(t) runService := service.NewJobRunService(logger, - jobRepo, jobRunRepo, nil, nil, nil, nil, nil) + nil, jobRunRepo, nil, nil, nil, nil, nil, nil) err := runService.UpdateJobState(ctx, event) assert.NotNil(t, err) @@ -291,8 +297,12 @@ func TestJobRunService(t *testing.T) { jobRunRepo.On("Create", ctx, tnnt, jobName, scheduledAtTimeStamp, slaDefinitionInSec).Return(fmt.Errorf("unable to create job run")).Once() defer jobRunRepo.AssertExpectations(t) + jobRepo := new(JobRepository) + jobRepo.On("GetJobDetails", ctx, projName, jobName).Return(&jobWithDetails, nil) + defer jobRepo.AssertExpectations(t) + runService := service.NewJobRunService(logger, - jobRepo, jobRunRepo, nil, nil, nil, nil, nil) + jobRepo, jobRunRepo, nil, nil, nil, nil, nil, nil) err := runService.UpdateJobState(ctx, event) assert.NotNil(t, err) @@ -304,8 +314,12 @@ func TestJobRunService(t *testing.T) { jobRunRepo.On("Create", ctx, tnnt, jobName, scheduledAtTimeStamp, slaDefinitionInSec).Return(nil) defer jobRunRepo.AssertExpectations(t) + jobRepo := new(JobRepository) + jobRepo.On("GetJobDetails", ctx, projName, jobName).Return(&jobWithDetails, nil) + defer jobRepo.AssertExpectations(t) + runService := service.NewJobRunService(logger, - jobRepo, jobRunRepo, nil, nil, nil, nil, nil) + jobRepo, jobRunRepo, nil, nil, nil, nil, nil, nil) err := runService.UpdateJobState(ctx, event) assert.NotNil(t, err) @@ -320,8 +334,16 @@ func TestJobRunService(t *testing.T) { jobRunRepo.On("UpdateMonitoring", ctx, jobRun.ID, monitoring).Return(nil) defer jobRunRepo.AssertExpectations(t) + eventHandler := newEventHandler(t) + eventHandler.On("HandleEvent", mock.Anything).Times(1) + defer eventHandler.AssertExpectations(t) + + jobRepo := new(JobRepository) + jobRepo.On("GetJobDetails", ctx, projName, jobName).Return(&jobWithDetails, nil) + defer jobRepo.AssertExpectations(t) + runService := service.NewJobRunService(logger, - jobRepo, jobRunRepo, nil, nil, nil, nil, nil) + jobRepo, jobRunRepo, nil, nil, nil, nil, nil, eventHandler) err := runService.UpdateJobState(ctx, event) assert.Nil(t, err) @@ -347,7 +369,7 @@ func TestJobRunService(t *testing.T) { defer jobRunRepo.AssertExpectations(t) runService := service.NewJobRunService(logger, - nil, jobRunRepo, nil, nil, nil, nil, nil) + nil, jobRunRepo, nil, nil, nil, nil, nil, nil) err := runService.UpdateJobState(ctx, event) assert.NotNil(t, err) @@ -370,7 +392,7 @@ func TestJobRunService(t *testing.T) { defer jobRunRepo.AssertExpectations(t) runService := service.NewJobRunService(logger, - nil, jobRunRepo, nil, nil, nil, nil, nil) + nil, jobRunRepo, nil, nil, nil, nil, nil, nil) err := runService.UpdateJobState(ctx, event) assert.NotNil(t, err) @@ -393,7 +415,7 @@ func TestJobRunService(t *testing.T) { defer jobRunRepo.AssertExpectations(t) runService := service.NewJobRunService(logger, - nil, jobRunRepo, nil, nil, nil, nil, nil) + nil, jobRunRepo, nil, nil, nil, nil, nil, nil) err := runService.UpdateJobState(ctx, event) assert.NotNil(t, err) @@ -422,6 +444,7 @@ func TestJobRunService(t *testing.T) { jobRunRepo := new(mockJobRunRepository) jobRunRepo.On("GetByScheduledAt", ctx, tnnt, jobName, scheduledAtTimeStamp).Return(&jobRun, nil) + jobRunRepo.On("UpdateState", ctx, jobRun.ID, scheduler.StateInProgress).Return(nil) defer jobRunRepo.AssertExpectations(t) t.Run("should pass creating new operator run ", func(t *testing.T) { @@ -429,8 +452,12 @@ func TestJobRunService(t *testing.T) { operatorRunRepository.On("CreateOperatorRun", ctx, event.OperatorName, scheduler.OperatorTask, jobRun.ID, eventTime).Return(nil) defer operatorRunRepository.AssertExpectations(t) + eventHandler := newEventHandler(t) + eventHandler.On("HandleEvent", mock.Anything).Times(1) + defer eventHandler.AssertExpectations(t) + runService := service.NewJobRunService(logger, - nil, jobRunRepo, nil, operatorRunRepository, nil, nil, nil) + nil, jobRunRepo, nil, operatorRunRepository, nil, nil, nil, eventHandler) err := runService.UpdateJobState(ctx, event) assert.Nil(t, err) @@ -461,10 +488,6 @@ func TestJobRunService(t *testing.T) { StartTime: time.Now(), } - jobRunRepo := new(mockJobRunRepository) - jobRunRepo.On("GetByScheduledAt", ctx, tnnt, jobName, scheduledAtTimeStamp).Return(&jobRun, nil) - defer jobRunRepo.AssertExpectations(t) - operatorRun := scheduler.OperatorRun{ ID: uuid.New(), Name: "task_bq2bq", @@ -477,8 +500,18 @@ func TestJobRunService(t *testing.T) { operatorRunRepository.On("GetOperatorRun", ctx, event.OperatorName, scheduler.OperatorTask, jobRun.ID).Return(nil, errors.NotFound(scheduler.EntityEvent, "operator not found in db")).Once() operatorRunRepository.On("CreateOperatorRun", ctx, event.OperatorName, scheduler.OperatorTask, jobRun.ID, eventTime).Return(fmt.Errorf("some error in creating operator run")) defer operatorRunRepository.AssertExpectations(t) + + jobRunRepo := new(mockJobRunRepository) + jobRunRepo.On("GetByScheduledAt", ctx, tnnt, jobName, scheduledAtTimeStamp).Return(&jobRun, nil) + jobRunRepo.On("UpdateState", ctx, jobRun.ID, scheduler.StateInProgress).Return(nil) + defer jobRunRepo.AssertExpectations(t) + + eventHandler := newEventHandler(t) + eventHandler.On("HandleEvent", mock.Anything).Times(1) + defer eventHandler.AssertExpectations(t) + runService := service.NewJobRunService(logger, - nil, jobRunRepo, nil, operatorRunRepository, nil, nil, nil) + nil, jobRunRepo, nil, operatorRunRepository, nil, nil, nil, eventHandler) err := runService.UpdateJobState(ctx, event) assert.NotNil(t, err) @@ -490,8 +523,18 @@ func TestJobRunService(t *testing.T) { operatorRunRepository.On("CreateOperatorRun", ctx, event.OperatorName, scheduler.OperatorTask, jobRun.ID, eventTime).Return(nil) operatorRunRepository.On("GetOperatorRun", ctx, event.OperatorName, scheduler.OperatorTask, jobRun.ID).Return(nil, fmt.Errorf("some error in getting operator run")).Once() defer operatorRunRepository.AssertExpectations(t) + + jobRunRepo := new(mockJobRunRepository) + jobRunRepo.On("GetByScheduledAt", ctx, tnnt, jobName, scheduledAtTimeStamp).Return(&jobRun, nil) + jobRunRepo.On("UpdateState", ctx, jobRun.ID, scheduler.StateInProgress).Return(nil) + defer jobRunRepo.AssertExpectations(t) + + eventHandler := newEventHandler(t) + eventHandler.On("HandleEvent", mock.Anything).Times(1) + defer eventHandler.AssertExpectations(t) + runService := service.NewJobRunService(logger, - nil, jobRunRepo, nil, operatorRunRepository, nil, nil, nil) + nil, jobRunRepo, nil, operatorRunRepository, nil, nil, nil, eventHandler) err := runService.UpdateJobState(ctx, event) assert.NotNil(t, err) @@ -502,8 +545,13 @@ func TestJobRunService(t *testing.T) { operatorRunRepository.On("GetOperatorRun", ctx, event.OperatorName, scheduler.OperatorTask, jobRun.ID).Return(&operatorRun, nil) operatorRunRepository.On("UpdateOperatorRun", ctx, scheduler.OperatorTask, operatorRun.ID, eventTime, scheduler.StateSuccess).Return(nil) defer operatorRunRepository.AssertExpectations(t) + + jobRunRepo := new(mockJobRunRepository) + jobRunRepo.On("GetByScheduledAt", ctx, tnnt, jobName, scheduledAtTimeStamp).Return(&jobRun, nil) + defer jobRunRepo.AssertExpectations(t) + runService := service.NewJobRunService(logger, - nil, jobRunRepo, nil, operatorRunRepository, nil, nil, nil) + nil, jobRunRepo, nil, operatorRunRepository, nil, nil, nil, nil) err := runService.UpdateJobState(ctx, event) assert.Nil(t, err) @@ -529,7 +577,7 @@ func TestJobRunService(t *testing.T) { defer jobRunRepo.AssertExpectations(t) runService := service.NewJobRunService(logger, - nil, jobRunRepo, nil, nil, nil, nil, nil) + nil, jobRunRepo, nil, nil, nil, nil, nil, nil) err := runService.UpdateJobState(ctx, event) assert.NotNil(t, err) @@ -566,7 +614,7 @@ func TestJobRunService(t *testing.T) { // operatorRunRepository.On("UpdateOperatorRun", ctx, scheduler.OperatorSensor, operatorRun.ID, eventTime, "success").Return(nil) defer operatorRunRepository.AssertExpectations(t) runService := service.NewJobRunService(logger, - nil, jobRunRepo, nil, operatorRunRepository, nil, nil, nil) + nil, jobRunRepo, nil, operatorRunRepository, nil, nil, nil, nil) err := runService.UpdateJobState(ctx, event) assert.NotNil(t, err) @@ -582,7 +630,7 @@ func TestJobRunService(t *testing.T) { defer jobRepo.AssertExpectations(t) runService := service.NewJobRunService(logger, - jobRepo, nil, nil, nil, nil, nil, nil) + jobRepo, nil, nil, nil, nil, nil, nil, nil) executorInput, err := runService.JobRunInput(ctx, projName, jobName, scheduler.RunConfig{}) assert.Nil(t, executorInput) assert.NotNil(t, err) @@ -640,7 +688,7 @@ func TestJobRunService(t *testing.T) { defer jobInputCompiler.AssertExpectations(t) runService := service.NewJobRunService(logger, - jobRepo, jobRunRepo, jobReplayRepo, nil, nil, nil, jobInputCompiler) + jobRepo, jobRunRepo, jobReplayRepo, nil, nil, nil, jobInputCompiler, nil) executorInput, err := runService.JobRunInput(ctx, projName, jobName, runConfig) assert.Equal(t, &dummyExecutorInput, executorInput) @@ -700,7 +748,7 @@ func TestJobRunService(t *testing.T) { defer jobInputCompiler.AssertExpectations(t) runService := service.NewJobRunService(logger, - jobRepo, jobRunRepo, jobReplayRepo, nil, nil, nil, jobInputCompiler) + jobRepo, jobRunRepo, jobReplayRepo, nil, nil, nil, jobInputCompiler, nil) executorInput, err := runService.JobRunInput(ctx, projName, jobName, runConfig) assert.Equal(t, &dummyExecutorInput, executorInput) @@ -753,7 +801,7 @@ func TestJobRunService(t *testing.T) { defer jobInputCompiler.AssertExpectations(t) runService := service.NewJobRunService(logger, - jobRepo, jobRunRepo, jobReplayRepo, nil, nil, nil, jobInputCompiler) + jobRepo, jobRunRepo, jobReplayRepo, nil, nil, nil, jobInputCompiler, nil) executorInput, err := runService.JobRunInput(ctx, projName, jobName, runConfig) assert.Equal(t, &dummyExecutorInput, executorInput) @@ -805,7 +853,7 @@ func TestJobRunService(t *testing.T) { defer jobInputCompiler.AssertExpectations(t) runService := service.NewJobRunService(logger, - jobRepo, jobRunRepo, jobReplayRepo, nil, nil, nil, jobInputCompiler) + jobRepo, jobRunRepo, jobReplayRepo, nil, nil, nil, jobInputCompiler, nil) executorInput, err := runService.JobRunInput(ctx, projName, jobName, runConfig) assert.Nil(t, err) @@ -838,10 +886,10 @@ func TestJobRunService(t *testing.T) { defer jobRepo.AssertExpectations(t) runService := service.NewJobRunService(logger, - jobRepo, nil, nil, nil, nil, nil, nil) + jobRepo, nil, nil, nil, nil, nil, nil, nil) returnedRuns, err := runService.GetJobRuns(ctx, projName, jobName, criteria) - assert.NotNil(t, err) - assert.EqualError(t, err, "unable to get job details from DB for jobName: sample_select, project:proj, error:some error in get job details ") + assert.Error(t, err) + assert.ErrorContains(t, err, "unable to get job details for jobName: sample_select, project:proj") assert.Nil(t, returnedRuns) }) t.Run("should not able to get job runs when scheduler returns empty response", func(t *testing.T) { @@ -876,10 +924,10 @@ func TestJobRunService(t *testing.T) { defer jobRepo.AssertExpectations(t) runService := service.NewJobRunService(logger, - jobRepo, nil, nil, nil, sch, nil, nil) + jobRepo, nil, nil, nil, sch, nil, nil, nil) returnedRuns, err := runService.GetJobRuns(ctx, projName, jobName, criteria) assert.Nil(t, err) - assert.Nil(t, nil, returnedRuns) + assert.Nil(t, returnedRuns) }) t.Run("should able to get job runs when scheduler returns valid response", func(t *testing.T) { tnnt, _ := tenant.NewTenant(projName.String(), namespaceName.String()) @@ -1011,8 +1059,7 @@ func TestJobRunService(t *testing.T) { jobRepo.On("GetJobDetails", ctx, projName, jobName).Return(&jobWithDetails, nil) defer jobRepo.AssertExpectations(t) runService := service.NewJobRunService(logger, - jobRepo, nil, nil, nil, - sch, nil, nil) + jobRepo, nil, nil, nil, sch, nil, nil, nil) returnedRuns, err := runService.GetJobRuns(ctx, projName, jobName, scenario.input) assert.Nil(t, err) assert.Equal(t, scenario.expectedResult, returnedRuns) @@ -1048,11 +1095,11 @@ func TestJobRunService(t *testing.T) { jobRepo.On("GetJobDetails", ctx, projName, jobName).Return(&jobWithDetails, nil) defer jobRepo.AssertExpectations(t) - runService := service.NewJobRunService(logger, jobRepo, nil, nil, nil, nil, nil, nil) + runService := service.NewJobRunService(logger, jobRepo, nil, nil, nil, nil, nil, nil, nil) returnedRuns, err := runService.GetJobRuns(ctx, projName, jobName, jobQuery) - assert.NotNil(t, err) - assert.EqualError(t, err, "invalid date range") - assert.Nil(t, nil, returnedRuns) + assert.Error(t, err) + assert.ErrorContains(t, err, "invalid date range, interval contains dates before job start") + assert.Nil(t, returnedRuns) }) t.Run("should not able to get job runs when invalid cron interval present at DB", func(t *testing.T) { tnnt, _ := tenant.NewTenant(projName.String(), namespaceName.String()) @@ -1083,11 +1130,11 @@ func TestJobRunService(t *testing.T) { jobRepo.On("GetJobDetails", ctx, projName, jobName).Return(&jobWithDetails, nil) defer jobRepo.AssertExpectations(t) - runService := service.NewJobRunService(logger, jobRepo, nil, nil, nil, nil, nil, nil) + runService := service.NewJobRunService(logger, jobRepo, nil, nil, nil, nil, nil, nil, nil) returnedRuns, err := runService.GetJobRuns(ctx, projName, jobName, jobQuery) - assert.NotNil(t, err) - assert.EqualError(t, err, "unable to parse job cron interval expected exactly 5 fields, found 2: [invalid interval]") - assert.Nil(t, nil, returnedRuns) + assert.Error(t, err) + assert.ErrorContains(t, err, "unable to parse job cron interval: expected exactly 5 fields, found 2: [invalid interval]") + assert.Nil(t, returnedRuns) }) t.Run("should not able to get job runs when no cron interval present at DB", func(t *testing.T) { tnnt, _ := tenant.NewTenant(projName.String(), namespaceName.String()) @@ -1117,11 +1164,11 @@ func TestJobRunService(t *testing.T) { jobRepo.On("GetJobDetails", ctx, projName, jobName).Return(&jobWithDetails, nil) defer jobRepo.AssertExpectations(t) - runService := service.NewJobRunService(logger, jobRepo, nil, nil, nil, nil, nil, nil) + runService := service.NewJobRunService(logger, jobRepo, nil, nil, nil, nil, nil, nil, nil) returnedRuns, err := runService.GetJobRuns(ctx, projName, jobName, jobQuery) - assert.NotNil(t, err) - assert.EqualError(t, err, "job schedule interval not found") - assert.Nil(t, nil, returnedRuns) + assert.Error(t, err) + assert.ErrorContains(t, err, "cannot get job runs, job interval is empty") + assert.Nil(t, returnedRuns) }) t.Run("should not able to get job runs when no start date present at DB", func(t *testing.T) { tnnt, _ := tenant.NewTenant(projName.String(), namespaceName.String()) @@ -1149,11 +1196,11 @@ func TestJobRunService(t *testing.T) { jobRepo := new(JobRepository) jobRepo.On("GetJobDetails", ctx, projName, jobName).Return(&jobWithDetails, nil) defer jobRepo.AssertExpectations(t) - runService := service.NewJobRunService(logger, jobRepo, nil, nil, nil, nil, nil, nil) + runService := service.NewJobRunService(logger, jobRepo, nil, nil, nil, nil, nil, nil, nil) returnedRuns, err := runService.GetJobRuns(ctx, projName, jobName, jobQuery) - assert.NotNil(t, err) - assert.EqualError(t, err, "job schedule startDate not found in job fetched from DB") - assert.Nil(t, nil, returnedRuns) + assert.Error(t, err) + assert.ErrorContains(t, err, "job schedule startDate not found in job") + assert.Nil(t, returnedRuns) }) t.Run("should able to get job runs when only last run is required", func(t *testing.T) { tnnt, _ := tenant.NewTenant(projName.String(), namespaceName.String()) @@ -1192,7 +1239,7 @@ func TestJobRunService(t *testing.T) { jobRepo.On("GetJobDetails", ctx, projName, jobName).Return(&jobWithDetails, nil) defer jobRepo.AssertExpectations(t) - runService := service.NewJobRunService(logger, jobRepo, nil, nil, nil, sch, nil, nil) + runService := service.NewJobRunService(logger, jobRepo, nil, nil, nil, sch, nil, nil, nil) returnedRuns, err := runService.GetJobRuns(ctx, projName, jobName, criteria) assert.Nil(t, err) assert.Equal(t, runs, returnedRuns) @@ -1259,6 +1306,11 @@ func (m *mockJobRunRepository) Update(ctx context.Context, jobRunID uuid.UUID, e return args.Error(0) } +func (m *mockJobRunRepository) UpdateState(ctx context.Context, jobRunID uuid.UUID, jobRunStatus scheduler.State) error { + args := m.Called(ctx, jobRunID, jobRunStatus) + return args.Error(0) +} + func (m *mockJobRunRepository) UpdateSLA(ctx context.Context, slaObjects []*scheduler.SLAObject) error { args := m.Called(ctx, slaObjects) return args.Error(0) @@ -1297,6 +1349,14 @@ func (j *JobRepository) GetAll(ctx context.Context, projectName tenant.ProjectNa return args.Get(0).([]*scheduler.JobWithDetails), args.Error(1) } +func (j *JobRepository) GetJobs(ctx context.Context, projectName tenant.ProjectName, jobs []string) ([]*scheduler.JobWithDetails, error) { + args := j.Called(ctx, projectName, jobs) + if args.Get(0) == nil { + return nil, args.Error(1) + } + return args.Get(0).([]*scheduler.JobWithDetails), args.Error(1) +} + type mockScheduler struct { mock.Mock } @@ -1345,3 +1405,25 @@ func (m *mockOperatorRunRepository) UpdateOperatorRun(ctx context.Context, opera args := m.Called(ctx, operator, jobRunID, eventTime, state) return args.Error(0) } + +type mockEventHandler struct { + mock.Mock +} + +func (m *mockEventHandler) HandleEvent(e moderator.Event) { + m.Called(e) +} + +type mockConstructorEventHandler interface { + mock.TestingT + Cleanup(func()) +} + +func newEventHandler(t mockConstructorEventHandler) *mockEventHandler { + mock := &mockEventHandler{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/scheduler/service/notification_service.go b/core/scheduler/service/notification_service.go index 2936735779..21a93aa3f3 100644 --- a/core/scheduler/service/notification_service.go +++ b/core/scheduler/service/notification_service.go @@ -6,24 +6,12 @@ import ( "io" "strings" - "github.com/odpf/salt/log" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/raystack/salt/log" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" -) - -var ( - jobFailureCounter = promauto.NewCounter(prometheus.CounterOpts{ - Name: "job_event_failure", - Help: "Event received for job failures by scheduler", - }) - jobSLAMissCounter = promauto.NewCounter(prometheus.CounterOpts{ - Name: "job_event_slamiss", - Help: "Event received for SLA miss by scheduler", - }) + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/telemetry" ) const ( @@ -46,6 +34,7 @@ type NotifyService struct { func (n *NotifyService) Push(ctx context.Context, event *scheduler.Event) error { jobDetails, err := n.jobRepo.GetJobDetails(ctx, event.Tenant.ProjectName(), event.JobName) if err != nil { + n.l.Error("error getting detail for job [%s]: %s", event.JobName, err) return err } notificationConfig := jobDetails.Alerts @@ -59,10 +48,12 @@ func (n *NotifyService) Push(ctx context.Context, event *scheduler.Event) error scheme := chanParts[0] route := chanParts[1] - n.l.Debug("notification event for job", "job spec name", event.JobName, "event", fmt.Sprintf("%v", event)) + n.l.Debug("notification event for job: %s , event: %+v", event.JobName, event) if plainTextSecretsList == nil { plainTextSecretsList, err = n.tenantService.GetSecrets(ctx, event.Tenant) if err != nil { + n.l.Error("error getting secrets for project [%s] namespace [%s]: %s", + event.Tenant.ProjectName().String(), event.Tenant.NamespaceName().String(), err) multierror.Append(err) continue } @@ -85,29 +76,30 @@ func (n *NotifyService) Push(ctx context.Context, event *scheduler.Event) error Secret: secret, Route: route, }); currErr != nil { - n.l.Error("Error: No notification event for job ", "current error", currErr) + n.l.Error("Error: No notification event for job current error: %s", currErr) multierror.Append(fmt.Errorf("notifyChannel.Notify: %s: %w", channel, currErr)) } } } + telemetry.NewCounter("jobrun_alerts_total", map[string]string{ + "project": event.Tenant.ProjectName().String(), + "namespace": event.Tenant.NamespaceName().String(), + "type": event.Type.String(), + }).Inc() } } - if event.Type.IsOfType(scheduler.EventCategoryJobFailure) { - jobFailureCounter.Inc() - } else if event.Type.IsOfType(scheduler.EventCategorySLAMiss) { - jobSLAMissCounter.Inc() - } - return errors.MultiToError(multierror) + return multierror.ToErr() } func (n *NotifyService) Close() error { - multierror := errors.NewMultiError("ErrorsInNotifyClose") + me := errors.NewMultiError("ErrorsInNotifyClose") for _, notify := range n.notifyChannels { if cerr := notify.Close(); cerr != nil { - multierror.Append(cerr) + n.l.Error("error closing notificication channel: %s", cerr) + me.Append(cerr) } } - return errors.MultiToError(multierror) + return me.ToErr() } func NewNotifyService(l log.Logger, jobRepo JobRepository, tenantService TenantService, notifyChan map[string]Notifier) *NotifyService { diff --git a/core/scheduler/service/notification_service_test.go b/core/scheduler/service/notification_service_test.go index 70f4c3719a..bc5a68e624 100644 --- a/core/scheduler/service/notification_service_test.go +++ b/core/scheduler/service/notification_service_test.go @@ -7,13 +7,13 @@ import ( "testing" "time" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/scheduler/service" - "github.com/odpf/optimus/core/tenant" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/scheduler/service" + "github.com/raystack/optimus/core/tenant" ) func TestNotificationService(t *testing.T) { @@ -33,7 +33,7 @@ func TestNotificationService(t *testing.T) { jobRepo.On("GetJobDetails", ctx, project.Name(), jobName).Return(nil, fmt.Errorf("some error")) defer jobRepo.AssertExpectations(t) - notifyService := service.NewNotifyService(nil, jobRepo, nil, nil) + notifyService := service.NewNotifyService(logger, jobRepo, nil, nil) event := &scheduler.Event{ JobName: jobName, diff --git a/core/scheduler/service/replay_manager.go b/core/scheduler/service/replay_manager.go index 69ebad5e2f..7bf7f5db5c 100644 --- a/core/scheduler/service/replay_manager.go +++ b/core/scheduler/service/replay_manager.go @@ -3,13 +3,13 @@ package service import ( "time" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/robfig/cron/v3" "golang.org/x/net/context" - "github.com/odpf/optimus/config" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/internal/errors" ) const ( @@ -49,7 +49,7 @@ func (m ReplayManager) Initialize() { if m.schedule != nil { _, err := m.schedule.AddFunc(syncInterval, m.StartReplayLoop) if err != nil { - m.l.Error("Failed to sync replay", "error", err) + m.l.Error("Failed to add function to cron schedule: %s", err) } m.schedule.Start() } @@ -80,7 +80,7 @@ func (m ReplayManager) checkTimedOutReplay(ctx context.Context) { scheduler.ReplayStateInProgress, scheduler.ReplayStatePartialReplayed, scheduler.ReplayStateReplayed, }) if err != nil { - m.l.Error("unable to get on going replay") + m.l.Error("error getting ongoing replay: %s", err) } for _, replay := range onGoingReplays { @@ -90,8 +90,7 @@ func (m ReplayManager) checkTimedOutReplay(ctx context.Context) { } message := "replay timed out" if err := m.replayRepository.UpdateReplayStatus(ctx, replay.ID(), scheduler.ReplayStateFailed, message); err != nil { - m.l.Error("unable to mark replay %s as failed due to time out", replay.ID()) + m.l.Error("unable to mark replay [%s] as failed due to time out", replay.ID()) } - m.l.Info("replay %s timed out. marked as failed.", replay.ID()) } } diff --git a/core/scheduler/service/replay_manager_test.go b/core/scheduler/service/replay_manager_test.go index 1af1588ae4..24d05ea760 100644 --- a/core/scheduler/service/replay_manager_test.go +++ b/core/scheduler/service/replay_manager_test.go @@ -6,13 +6,13 @@ import ( "time" "github.com/google/uuid" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "golang.org/x/net/context" - "github.com/odpf/optimus/config" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/scheduler/service" - "github.com/odpf/optimus/core/tenant" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/scheduler/service" + "github.com/raystack/optimus/core/tenant" ) func TestReplayManager(t *testing.T) { diff --git a/core/scheduler/service/replay_service.go b/core/scheduler/service/replay_service.go index 20fb897f1d..d9d30d23ba 100644 --- a/core/scheduler/service/replay_service.go +++ b/core/scheduler/service/replay_service.go @@ -4,11 +4,20 @@ import ( "fmt" "github.com/google/uuid" + "github.com/raystack/salt/log" "golang.org/x/net/context" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/lib/cron" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/lib/cron" + "github.com/raystack/optimus/internal/telemetry" +) + +const ( + getReplaysDayLimit = 30 // TODO: make it configurable via cli + + metricJobReplay = "jobrun_replay_requests_total" ) type ReplayRepository interface { @@ -18,6 +27,8 @@ type ReplayRepository interface { GetReplayToExecute(context.Context) (*scheduler.ReplayWithRun, error) GetReplayRequestsByStatus(ctx context.Context, statusList []scheduler.ReplayState) ([]*scheduler.Replay, error) + GetReplaysByProject(ctx context.Context, projectName tenant.ProjectName, dayLimits int) ([]*scheduler.Replay, error) + GetReplayByID(ctx context.Context, replayID uuid.UUID) (*scheduler.ReplayWithRun, error) } type ReplayValidator interface { @@ -29,28 +40,63 @@ type ReplayService struct { jobRepo JobRepository validator ReplayValidator + + logger log.Logger } func (r ReplayService) CreateReplay(ctx context.Context, tenant tenant.Tenant, jobName scheduler.JobName, config *scheduler.ReplayConfig) (replayID uuid.UUID, err error) { subjectJob, err := r.jobRepo.GetJobDetails(ctx, tenant.ProjectName(), jobName) if err != nil { - return uuid.Nil, fmt.Errorf("unable to get job details from DB for jobName: %s, project:%s, error:%w ", jobName, tenant.ProjectName().String(), err) + r.logger.Error("error getting job details of [%s]: %s", jobName.String(), err) + return uuid.Nil, errors.AddErrContext(err, scheduler.EntityReplay, + fmt.Sprintf("unable to get job details for jobName: %s, project:%s", jobName, tenant.ProjectName().String())) + } + + if subjectJob.Job.Tenant.NamespaceName() != tenant.NamespaceName() { + r.logger.Error("job [%s] resides in namespace [%s], expecting it under [%s]", jobName, subjectJob.Job.Tenant.NamespaceName(), tenant.NamespaceName()) + return uuid.Nil, errors.InvalidArgument(scheduler.EntityReplay, fmt.Sprintf("job %s does not exist in %s namespace", jobName, tenant.NamespaceName().String())) } jobCron, err := cron.ParseCronSchedule(subjectJob.Schedule.Interval) if err != nil { - return uuid.Nil, fmt.Errorf("encountered unexpected error when parsing job cron interval for job %s: %w", jobName, err) + r.logger.Error("error parsing cron schedule for interval [%s]: %s", subjectJob.Schedule.Interval, err) + return uuid.Nil, errors.InternalError(scheduler.EntityReplay, "invalid cron interval for "+jobName.String(), err) } replayReq := scheduler.NewReplayRequest(jobName, tenant, config, scheduler.ReplayStateCreated) if err := r.validator.Validate(ctx, replayReq, jobCron); err != nil { + r.logger.Error("error validating replay request: %s", err) return uuid.Nil, err } runs := getExpectedRuns(jobCron, config.StartTime, config.EndTime) - return r.replayRepo.RegisterReplay(ctx, replayReq, runs) + replayID, err = r.replayRepo.RegisterReplay(ctx, replayReq, runs) + if err != nil { + return uuid.Nil, err + } + + telemetry.NewCounter(metricJobReplay, map[string]string{ + "project": tenant.ProjectName().String(), + "namespace": tenant.NamespaceName().String(), + "job": jobName.String(), + "status": replayReq.State().String(), + }).Inc() + return replayID, nil +} + +func (r ReplayService) GetReplayList(ctx context.Context, projectName tenant.ProjectName) (replays []*scheduler.Replay, err error) { + return r.replayRepo.GetReplaysByProject(ctx, projectName, getReplaysDayLimit) +} + +func (r ReplayService) GetReplayByID(ctx context.Context, replayID uuid.UUID) (*scheduler.ReplayWithRun, error) { + replayWithRun, err := r.replayRepo.GetReplayByID(ctx, replayID) + if err != nil { + return nil, err + } + replayWithRun.Runs = scheduler.JobRunStatusList(replayWithRun.Runs).GetSortedRunsByScheduledAt() + return replayWithRun, nil } -func NewReplayService(replayRepo ReplayRepository, jobRepo JobRepository, validator ReplayValidator) *ReplayService { - return &ReplayService{replayRepo: replayRepo, jobRepo: jobRepo, validator: validator} +func NewReplayService(replayRepo ReplayRepository, jobRepo JobRepository, validator ReplayValidator, logger log.Logger) *ReplayService { + return &ReplayService{replayRepo: replayRepo, jobRepo: jobRepo, validator: validator, logger: logger} } diff --git a/core/scheduler/service/replay_service_test.go b/core/scheduler/service/replay_service_test.go index 9fee3db3bf..3f75da6e7b 100644 --- a/core/scheduler/service/replay_service_test.go +++ b/core/scheduler/service/replay_service_test.go @@ -7,13 +7,15 @@ import ( "time" "github.com/google/uuid" + "github.com/raystack/salt/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/scheduler/service" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/lib/cron" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/scheduler/service" + "github.com/raystack/optimus/core/tenant" + errs "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/lib/cron" ) func TestReplayService(t *testing.T) { @@ -47,6 +49,8 @@ func TestReplayService(t *testing.T) { jobCronStr := "0 12 * * *" jobCron, _ := cron.ParseCronSchedule(jobCronStr) + logger := log.NewLogrus() + t.Run("CreateReplay", func(t *testing.T) { t.Run("should return replay ID if replay created successfully", func(t *testing.T) { replayRepository := new(ReplayRepository) @@ -71,7 +75,7 @@ func TestReplayService(t *testing.T) { replayValidator.On("Validate", ctx, replayReq, jobCron).Return(nil) replayRepository.On("RegisterReplay", ctx, replayReq, replayRuns).Return(replayID, nil) - replayService := service.NewReplayService(replayRepository, jobRepository, replayValidator) + replayService := service.NewReplayService(replayRepository, jobRepository, replayValidator, logger) result, err := replayService.CreateReplay(ctx, tnnt, jobName, replayConfig) assert.NoError(t, err) assert.Equal(t, replayID, result) @@ -92,7 +96,7 @@ func TestReplayService(t *testing.T) { jobRepository.On("GetJobDetails", ctx, projName, jobName).Return(jobWithDetails, nil) replayValidator.On("Validate", ctx, replayReq, jobCron).Return(errors.New("not passed validation")) - replayService := service.NewReplayService(replayRepository, jobRepository, replayValidator) + replayService := service.NewReplayService(replayRepository, jobRepository, replayValidator, logger) result, err := replayService.CreateReplay(ctx, tnnt, jobName, replayConfig) assert.ErrorContains(t, err, "not passed validation") assert.Equal(t, uuid.Nil, result) @@ -111,11 +115,107 @@ func TestReplayService(t *testing.T) { internalErr := errors.New("internal error") jobRepository.On("GetJobDetails", ctx, projName, jobName).Return(nil, internalErr) - replayService := service.NewReplayService(replayRepository, jobRepository, replayValidator) + replayService := service.NewReplayService(replayRepository, jobRepository, replayValidator, logger) result, err := replayService.CreateReplay(ctx, tnnt, jobName, replayConfig) assert.ErrorIs(t, err, internalErr) assert.Equal(t, uuid.Nil, result) }) + + t.Run("should return error if namespace name is not match", func(t *testing.T) { + replayRepository := new(ReplayRepository) + defer replayRepository.AssertExpectations(t) + + jobRepository := new(JobRepository) + defer jobRepository.AssertExpectations(t) + + replayValidator := new(ReplayValidator) + defer replayValidator.AssertExpectations(t) + + invalidTenant, _ := tenant.NewTenant(projName.String(), "invalid-namespace") + + jobRepository.On("GetJobDetails", ctx, projName, jobName).Return(jobWithDetails, nil) + + replayService := service.NewReplayService(replayRepository, jobRepository, replayValidator, logger) + result, err := replayService.CreateReplay(ctx, invalidTenant, jobName, replayConfig) + assert.ErrorContains(t, err, "job sample_select does not exist in invalid-namespace namespace") + assert.Equal(t, uuid.Nil, result) + }) + }) + t.Run("GetReplayList", func(t *testing.T) { + t.Run("should return replay list with no error", func(t *testing.T) { + replayConfig := scheduler.NewReplayConfig(startTime, endTime, true, replayJobConfig, description) + replay1 := scheduler.NewReplayRequest("sample-job-A", tnnt, replayConfig, scheduler.ReplayStateInProgress) + replay2 := scheduler.NewReplayRequest("sample-job-B", tnnt, replayConfig, scheduler.ReplayStateCreated) + replay3 := scheduler.NewReplayRequest("sample-job-C", tnnt, replayConfig, scheduler.ReplayStateFailed) + replays := []*scheduler.Replay{replay1, replay2, replay3} + replayRepository := new(ReplayRepository) + replayRepository.On("GetReplaysByProject", ctx, mock.Anything, mock.Anything).Return(replays, nil) + defer replayRepository.AssertExpectations(t) + + replayService := service.NewReplayService(replayRepository, nil, nil, logger) + result, err := replayService.GetReplayList(ctx, tnnt.ProjectName()) + assert.NoError(t, err) + assert.Len(t, result, 3) + }) + + t.Run("should return error when get replay by project is fail", func(t *testing.T) { + replayRepository := new(ReplayRepository) + replayRepository.On("GetReplaysByProject", ctx, mock.Anything, mock.Anything).Return(nil, errors.New("some error")) + defer replayRepository.AssertExpectations(t) + + replayService := service.NewReplayService(replayRepository, nil, nil, logger) + result, err := replayService.GetReplayList(ctx, tnnt.ProjectName()) + assert.Error(t, err) + assert.Nil(t, result) + }) + }) + t.Run("GetReplayByID", func(t *testing.T) { + t.Run("returns empty if replay is not found", func(t *testing.T) { + replayRepository := new(ReplayRepository) + defer replayRepository.AssertExpectations(t) + + replayID := uuid.New() + replayRepository.On("GetReplayByID", ctx, replayID).Return(nil, errs.NotFound("entity", "not found")) + + replayService := service.NewReplayService(replayRepository, nil, nil, logger) + result, err := replayService.GetReplayByID(ctx, replayID) + assert.True(t, errs.IsErrorType(err, errs.ErrNotFound)) + assert.Empty(t, result) + }) + t.Run("returns err if get replay by id on replay repo is failed", func(t *testing.T) { + replayRepository := new(ReplayRepository) + defer replayRepository.AssertExpectations(t) + + replayID := uuid.New() + replayRepository.On("GetReplayByID", ctx, replayID).Return(nil, errors.New("internal error")) + + replayService := service.NewReplayService(replayRepository, nil, nil, logger) + result, err := replayService.GetReplayByID(ctx, replayID) + assert.Error(t, err) + assert.Nil(t, result) + }) + t.Run("returns success if replay with runs exist", func(t *testing.T) { + replayRepository := new(ReplayRepository) + defer replayRepository.AssertExpectations(t) + + replayID := uuid.New() + replay := scheduler.NewReplay(replayID, jobName, tnnt, replayConfig, scheduler.ReplayStateInProgress, startTime) + replayRepository.On("GetReplayByID", ctx, replayID).Return(&scheduler.ReplayWithRun{ + Replay: replay, + Runs: []*scheduler.JobRunStatus{ + { + ScheduledAt: startTime, + State: scheduler.StatePending, + }, + }, + }, nil) + + replayService := service.NewReplayService(replayRepository, nil, nil, logger) + result, err := replayService.GetReplayByID(ctx, replayID) + assert.NoError(t, err) + assert.NotNil(t, result) + assert.NotEmpty(t, result) + }) }) } @@ -124,6 +224,58 @@ type ReplayRepository struct { mock.Mock } +// GetReplaysByProject provides a mock function with given fields: ctx, projectName, dayLimits +func (_m *ReplayRepository) GetReplaysByProject(ctx context.Context, projectName tenant.ProjectName, dayLimits int) ([]*scheduler.Replay, error) { + ret := _m.Called(ctx, projectName, dayLimits) + + var r0 []*scheduler.Replay + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, tenant.ProjectName, int) ([]*scheduler.Replay, error)); ok { + return rf(ctx, projectName, dayLimits) + } + if rf, ok := ret.Get(0).(func(context.Context, tenant.ProjectName, int) []*scheduler.Replay); ok { + r0 = rf(ctx, projectName, dayLimits) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*scheduler.Replay) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, tenant.ProjectName, int) error); ok { + r1 = rf(ctx, projectName, dayLimits) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetReplayByID provides a mock function with given fields: ctx, replayID +func (_m *ReplayRepository) GetReplayByID(ctx context.Context, replayID uuid.UUID) (*scheduler.ReplayWithRun, error) { + ret := _m.Called(ctx, replayID) + + var r0 *scheduler.ReplayWithRun + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID) (*scheduler.ReplayWithRun, error)); ok { + return rf(ctx, replayID) + } + if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID) *scheduler.ReplayWithRun); ok { + r0 = rf(ctx, replayID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*scheduler.ReplayWithRun) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, uuid.UUID) error); ok { + r1 = rf(ctx, replayID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetReplayRequestsByStatus provides a mock function with given fields: ctx, statusList func (_m *ReplayRepository) GetReplayRequestsByStatus(ctx context.Context, statusList []scheduler.ReplayState) ([]*scheduler.Replay, error) { ret := _m.Called(ctx, statusList) diff --git a/core/scheduler/service/replay_validator.go b/core/scheduler/service/replay_validator.go index 721c3d8e19..c995f651f2 100644 --- a/core/scheduler/service/replay_validator.go +++ b/core/scheduler/service/replay_validator.go @@ -1,11 +1,14 @@ package service import ( + "fmt" + "time" + "golang.org/x/net/context" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/internal/errors" - "github.com/odpf/optimus/internal/lib/cron" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/lib/cron" ) var replayStatusToValidate = []scheduler.ReplayState{ @@ -16,13 +19,18 @@ var replayStatusToValidate = []scheduler.ReplayState{ type Validator struct { replayRepository ReplayRepository scheduler ReplayScheduler + jobRepo JobRepository } -func NewValidator(replayRepository ReplayRepository, scheduler ReplayScheduler) *Validator { - return &Validator{replayRepository: replayRepository, scheduler: scheduler} +func NewValidator(replayRepository ReplayRepository, scheduler ReplayScheduler, jobRepo JobRepository) *Validator { + return &Validator{replayRepository: replayRepository, scheduler: scheduler, jobRepo: jobRepo} } func (v Validator) Validate(ctx context.Context, replayRequest *scheduler.Replay, jobCron *cron.ScheduleSpec) error { + if err := v.validateDateRange(ctx, replayRequest); err != nil { + return err + } + if err := v.validateConflictedReplay(ctx, replayRequest); err != nil { return err } @@ -30,6 +38,31 @@ func (v Validator) Validate(ctx context.Context, replayRequest *scheduler.Replay return v.validateConflictedRun(ctx, replayRequest, jobCron) } +func (v Validator) validateDateRange(ctx context.Context, replayRequest *scheduler.Replay) error { + jobSpec, err := v.jobRepo.GetJobDetails(ctx, replayRequest.Tenant().ProjectName(), replayRequest.JobName()) + if err != nil { + return err + } + replayStartDate := replayRequest.Config().StartTime.UTC() + replayEndDate := replayRequest.Config().EndTime.UTC() + jobStartDate := jobSpec.Schedule.StartDate.UTC() + jobEndDate := time.Now().UTC() + // time bound for end date + if jobSpec.Schedule.EndDate != nil && jobSpec.Schedule.EndDate.UTC().Before(jobEndDate) { + jobEndDate = jobSpec.Schedule.EndDate.UTC() + } + + if replayStartDate.Before(jobStartDate) { + return errors.NewError(errors.ErrFailedPrecond, scheduler.EntityReplay, fmt.Sprintf("replay start date (%s) is not allowed to be set before job start date (%s)", replayStartDate.String(), jobStartDate.String())) + } + + if replayEndDate.After(jobEndDate) { + return errors.NewError(errors.ErrFailedPrecond, scheduler.EntityReplay, fmt.Sprintf("replay end date (%s) is not allowed to be set after the date (%s)", replayEndDate.String(), jobEndDate.String())) + } + + return nil +} + func (v Validator) validateConflictedReplay(ctx context.Context, replayRequest *scheduler.Replay) error { onGoingReplays, err := v.replayRepository.GetReplayRequestsByStatus(ctx, replayStatusToValidate) if err != nil { diff --git a/core/scheduler/service/replay_validator_test.go b/core/scheduler/service/replay_validator_test.go index 778cac1fe2..0699c7cada 100644 --- a/core/scheduler/service/replay_validator_test.go +++ b/core/scheduler/service/replay_validator_test.go @@ -8,10 +8,10 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/scheduler/service" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/lib/cron" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/scheduler/service" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/lib/cron" ) func TestReplayValidator(t *testing.T) { @@ -41,13 +41,16 @@ func TestReplayValidator(t *testing.T) { replayReq := scheduler.NewReplayRequest(jobName, tnnt, replayConfig, scheduler.ReplayStateCreated) t.Run("Validate", func(t *testing.T) { - t.Run("should return nil if no conflict replay or conflict run found", func(t *testing.T) { + t.Run("should return nil if validation valid", func(t *testing.T) { replayRepository := new(ReplayRepository) defer replayRepository.AssertExpectations(t) sch := new(mockReplayScheduler) defer sch.AssertExpectations(t) + jobRepository := new(JobRepository) + defer jobRepository.AssertExpectations(t) + onGoingReplayConfig := scheduler.NewReplayConfig(time.Now(), time.Now(), parallel, replayJobConfig, description) onGoingReplay := []*scheduler.Replay{ scheduler.NewReplayRequest(jobName, tnnt, onGoingReplayConfig, scheduler.ReplayStateCreated), @@ -60,13 +63,62 @@ func TestReplayValidator(t *testing.T) { }, } + jobRepository.On("GetJobDetails", ctx, replayReq.Tenant().ProjectName(), replayReq.JobName()).Return(&scheduler.JobWithDetails{ + Schedule: &scheduler.Schedule{ + StartDate: startTime, + EndDate: &endTime, + }, + }, nil) replayRepository.On("GetReplayRequestsByStatus", ctx, replayStatusToValidate).Return(onGoingReplay, nil) sch.On("GetJobRuns", ctx, tnnt, runsCriteriaJobA, jobCron).Return(currentRuns, nil) - validator := service.NewValidator(replayRepository, sch) + validator := service.NewValidator(replayRepository, sch, jobRepository) err := validator.Validate(ctx, replayReq, jobCron) assert.NoError(t, err) }) + t.Run("should return error if start date of replay is before the start date of the job", func(t *testing.T) { + replayRepository := new(ReplayRepository) + defer replayRepository.AssertExpectations(t) + + sch := new(mockReplayScheduler) + defer sch.AssertExpectations(t) + + jobRepository := new(JobRepository) + defer jobRepository.AssertExpectations(t) + + jobRepository.On("GetJobDetails", ctx, replayReq.Tenant().ProjectName(), replayReq.JobName()).Return(&scheduler.JobWithDetails{ + Schedule: &scheduler.Schedule{ + StartDate: startTime.Add(1 * time.Second), // start date 1 second ahead of replay + EndDate: &endTime, + }, + }, nil) + + validator := service.NewValidator(replayRepository, sch, jobRepository) + err := validator.Validate(ctx, replayReq, jobCron) + assert.ErrorContains(t, err, "replay start date") + }) + t.Run("should return error if end date of replay is on the future", func(t *testing.T) { + replayRepository := new(ReplayRepository) + defer replayRepository.AssertExpectations(t) + + sch := new(mockReplayScheduler) + defer sch.AssertExpectations(t) + + jobRepository := new(JobRepository) + defer jobRepository.AssertExpectations(t) + + schEndTime := endTime.Add(-1 * time.Second) + jobRepository.On("GetJobDetails", ctx, replayReq.Tenant().ProjectName(), replayReq.JobName()).Return(&scheduler.JobWithDetails{ + Schedule: &scheduler.Schedule{ + StartDate: startTime, + EndDate: &schEndTime, // end date 1 second prior of replay + }, + }, nil) + + validator := service.NewValidator(replayRepository, sch, jobRepository) + err := validator.Validate(ctx, replayReq, jobCron) + assert.ErrorContains(t, err, "replay end date") + }) t.Run("should return error if conflict replay found", func(t *testing.T) { replayRepository := new(ReplayRepository) defer replayRepository.AssertExpectations(t) @@ -74,13 +126,23 @@ func TestReplayValidator(t *testing.T) { sch := new(mockReplayScheduler) defer sch.AssertExpectations(t) + jobRepository := new(JobRepository) + defer jobRepository.AssertExpectations(t) + onGoingReplay := []*scheduler.Replay{ scheduler.NewReplayRequest(jobName, tnnt, replayConfig, scheduler.ReplayStateInProgress), } + jobRepository.On("GetJobDetails", ctx, replayReq.Tenant().ProjectName(), replayReq.JobName()).Return(&scheduler.JobWithDetails{ + Schedule: &scheduler.Schedule{ + StartDate: startTime, + EndDate: &endTime, + }, + }, nil) + replayRepository.On("GetReplayRequestsByStatus", ctx, replayStatusToValidate).Return(onGoingReplay, nil) - validator := service.NewValidator(replayRepository, sch) + validator := service.NewValidator(replayRepository, sch, jobRepository) err := validator.Validate(ctx, replayReq, jobCron) assert.ErrorContains(t, err, "conflicted replay") }) @@ -91,6 +153,9 @@ func TestReplayValidator(t *testing.T) { sch := new(mockReplayScheduler) defer sch.AssertExpectations(t) + jobRepository := new(JobRepository) + defer jobRepository.AssertExpectations(t) + onGoingReplayConfig := scheduler.NewReplayConfig(time.Now(), time.Now(), parallel, replayJobConfig, description) onGoingReplay := []*scheduler.Replay{ scheduler.NewReplayRequest(jobName, tnnt, onGoingReplayConfig, scheduler.ReplayStateCreated), @@ -102,10 +167,16 @@ func TestReplayValidator(t *testing.T) { }, } + jobRepository.On("GetJobDetails", ctx, replayReq.Tenant().ProjectName(), replayReq.JobName()).Return(&scheduler.JobWithDetails{ + Schedule: &scheduler.Schedule{ + StartDate: startTime, + EndDate: &endTime, + }, + }, nil) replayRepository.On("GetReplayRequestsByStatus", ctx, replayStatusToValidate).Return(onGoingReplay, nil) sch.On("GetJobRuns", ctx, tnnt, runsCriteriaJobA, jobCron).Return(currentRuns, nil) - validator := service.NewValidator(replayRepository, sch) + validator := service.NewValidator(replayRepository, sch, jobRepository) err := validator.Validate(ctx, replayReq, jobCron) assert.ErrorContains(t, err, "conflicted job run found") }) @@ -116,10 +187,19 @@ func TestReplayValidator(t *testing.T) { sch := new(mockReplayScheduler) defer sch.AssertExpectations(t) + jobRepository := new(JobRepository) + defer jobRepository.AssertExpectations(t) + internalErr := errors.New("internal error") + jobRepository.On("GetJobDetails", ctx, replayReq.Tenant().ProjectName(), replayReq.JobName()).Return(&scheduler.JobWithDetails{ + Schedule: &scheduler.Schedule{ + StartDate: startTime, + EndDate: &endTime, + }, + }, nil) replayRepository.On("GetReplayRequestsByStatus", ctx, replayStatusToValidate).Return(nil, internalErr) - validator := service.NewValidator(replayRepository, sch) + validator := service.NewValidator(replayRepository, sch, jobRepository) err := validator.Validate(ctx, replayReq, jobCron) assert.ErrorIs(t, err, internalErr) }) @@ -130,6 +210,9 @@ func TestReplayValidator(t *testing.T) { sch := new(mockReplayScheduler) defer sch.AssertExpectations(t) + jobRepository := new(JobRepository) + defer jobRepository.AssertExpectations(t) + onGoingReplayConfig := scheduler.NewReplayConfig(time.Now(), time.Now(), parallel, map[string]string{}, description) onGoingReplay := []*scheduler.Replay{ scheduler.NewReplayRequest(jobName, tnnt, onGoingReplayConfig, scheduler.ReplayStateCreated), @@ -138,9 +221,15 @@ func TestReplayValidator(t *testing.T) { replayRepository.On("GetReplayRequestsByStatus", ctx, replayStatusToValidate).Return(onGoingReplay, nil) internalErr := errors.New("internal error") + jobRepository.On("GetJobDetails", ctx, replayReq.Tenant().ProjectName(), replayReq.JobName()).Return(&scheduler.JobWithDetails{ + Schedule: &scheduler.Schedule{ + StartDate: startTime, + EndDate: &endTime, + }, + }, nil) sch.On("GetJobRuns", ctx, tnnt, runsCriteriaJobA, jobCron).Return(nil, internalErr) - validator := service.NewValidator(replayRepository, sch) + validator := service.NewValidator(replayRepository, sch, jobRepository) err := validator.Validate(ctx, replayReq, jobCron) assert.ErrorIs(t, err, internalErr) }) diff --git a/core/scheduler/service/replay_worker.go b/core/scheduler/service/replay_worker.go index aabf4e550b..e8ea708692 100644 --- a/core/scheduler/service/replay_worker.go +++ b/core/scheduler/service/replay_worker.go @@ -5,19 +5,26 @@ import ( "time" "github.com/google/uuid" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "golang.org/x/net/context" - "github.com/odpf/optimus/config" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/lib/cron" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/lib/cron" + "github.com/raystack/optimus/internal/telemetry" +) + +const ( + prefixReplayed = "replayed" ) type ReplayScheduler interface { Clear(ctx context.Context, t tenant.Tenant, jobName scheduler.JobName, scheduledAt time.Time) error ClearBatch(ctx context.Context, t tenant.Tenant, jobName scheduler.JobName, startTime, endTime time.Time) error + CreateRun(ctx context.Context, tnnt tenant.Tenant, jobName scheduler.JobName, executionTime time.Time, dagRunIDPrefix string) error GetJobRuns(ctx context.Context, t tenant.Tenant, criteria *scheduler.JobRunsCriteria, jobCron *cron.ScheduleSpec) ([]*scheduler.JobRunStatus, error) } @@ -46,8 +53,9 @@ func (w ReplayWorker) Process(replayReq *scheduler.ReplayWithRun) { w.l.Debug("processing replay request %s with status %s", replayReq.Replay.ID().String(), replayReq.Replay.State().String()) jobCron, err := w.getJobCron(ctx, replayReq) if err != nil { - w.l.Error(fmt.Sprintf("unable to get cron value for job %s: %s", replayReq.Replay.JobName(), err.Error()), "replay_id", replayReq.Replay.ID()) + w.l.Error("unable to get cron value for job [%s] replay id [%s]: %s", replayReq.Replay.JobName().String(), replayReq.Replay.ID().String(), err) w.updateReplayAsFailed(ctx, replayReq.Replay.ID(), err.Error()) + raiseReplayMetric(replayReq.Replay.Tenant(), replayReq.Replay.JobName(), scheduler.ReplayStateFailed) return } @@ -61,28 +69,68 @@ func (w ReplayWorker) Process(replayReq *scheduler.ReplayWithRun) { } if err != nil { + w.l.Error("error encountered when processing replay request: %s", err) w.updateReplayAsFailed(ctx, replayReq.Replay.ID(), err.Error()) + raiseReplayMetric(replayReq.Replay.Tenant(), replayReq.Replay.JobName(), scheduler.ReplayStateFailed) } } +func (w ReplayWorker) createMissingRuns(ctx context.Context, replayReq *scheduler.ReplayWithRun, jobCron *cron.ScheduleSpec) ([]*scheduler.JobRunStatus, error) { + createdRuns := []*scheduler.JobRunStatus{} + + // fetch runs within range of replay range + existedRuns, err := w.fetchRuns(ctx, replayReq, jobCron) + if err != nil { + return nil, err + } + + // check each runs if there's no existing run from the above + existedRunsMap := scheduler.JobRunStatusList(existedRuns).ToRunStatusMap() + for _, run := range replayReq.Runs { + if _, ok := existedRunsMap[run.ScheduledAt]; !ok { + // create any missing runs + if err := w.scheduler.CreateRun(ctx, replayReq.Replay.Tenant(), replayReq.Replay.JobName(), run.ScheduledAt, prefixReplayed); err != nil { + return nil, err + } + run.State = scheduler.StateInProgress + createdRuns = append(createdRuns, run) + } + } + return createdRuns, nil +} + func (w ReplayWorker) processNewReplayRequest(ctx context.Context, replayReq *scheduler.ReplayWithRun, jobCron *cron.ScheduleSpec) (err error) { state := scheduler.ReplayStateReplayed + if !replayReq.Replay.Config().Parallel && len(replayReq.Runs) > 1 { + state = scheduler.ReplayStatePartialReplayed + } var updatedRuns []*scheduler.JobRunStatus + createdRuns, err := w.createMissingRuns(ctx, replayReq, jobCron) + if err != nil { + return err + } + if len(createdRuns) > 0 { + createdRunMap := scheduler.JobRunStatusList(createdRuns).ToRunStatusMap() + replayReq.Runs = scheduler.JobRunStatusList(replayReq.Runs).MergeWithUpdatedRuns(createdRunMap) + if err := w.replayRepo.UpdateReplay(ctx, replayReq.Replay.ID(), state, replayReq.Runs, ""); err != nil { + return err + } + } if replayReq.Replay.Config().Parallel { updatedRuns, err = w.processNewReplayRequestParallel(ctx, replayReq, jobCron) } else { updatedRuns, err = w.processNewReplayRequestSequential(ctx, replayReq, jobCron) - if len(replayReq.Runs) > 1 { - state = scheduler.ReplayStatePartialReplayed - } } if err != nil { + w.l.Error("error processing new replay: %s", err) return err } + if err := w.replayRepo.UpdateReplay(ctx, replayReq.Replay.ID(), state, updatedRuns, ""); err != nil { - w.l.Error("unable to update replay state", "replay_id", replayReq.Replay.ID()) + w.l.Error("unable to update replay state for replay_id [%s]: %s", replayReq.Replay.ID().String(), err) return err } + raiseReplayMetric(replayReq.Replay.Tenant(), replayReq.Replay.JobName(), state) return nil } @@ -90,7 +138,7 @@ func (w ReplayWorker) processNewReplayRequestParallel(ctx context.Context, repla startLogicalTime := replayReq.GetFirstExecutableRun().GetLogicalTime(jobCron) endLogicalTime := replayReq.GetLastExecutableRun().GetLogicalTime(jobCron) if err := w.scheduler.ClearBatch(ctx, replayReq.Replay.Tenant(), replayReq.Replay.JobName(), startLogicalTime, endLogicalTime); err != nil { - w.l.Error("unable to clear job run for replay", "replay_id", replayReq.Replay.ID()) + w.l.Error("unable to clear job run for replay with replay_id [%s]: %s", replayReq.Replay.ID().String(), err) return nil, err } @@ -98,21 +146,24 @@ func (w ReplayWorker) processNewReplayRequestParallel(ctx context.Context, repla var updatedRuns []*scheduler.JobRunStatus for _, run := range replayReq.Runs { - updatedRuns = append(updatedRuns, &scheduler.JobRunStatus{ScheduledAt: run.ScheduledAt, State: scheduler.StateReplayed}) + updatedRuns = append(updatedRuns, &scheduler.JobRunStatus{ScheduledAt: run.ScheduledAt, State: scheduler.StateInProgress}) } return updatedRuns, nil } func (w ReplayWorker) processNewReplayRequestSequential(ctx context.Context, replayReq *scheduler.ReplayWithRun, jobCron *cron.ScheduleSpec) ([]*scheduler.JobRunStatus, error) { runToClear := replayReq.GetFirstExecutableRun() + if runToClear == nil { + return replayReq.Runs, nil + } if err := w.scheduler.Clear(ctx, replayReq.Replay.Tenant(), replayReq.Replay.JobName(), runToClear.GetLogicalTime(jobCron)); err != nil { - w.l.Error("unable to clear job run for replay", "replay_id", replayReq.Replay.ID()) + w.l.Error("unable to clear job run for replay with replay_id [%s]: %s", replayReq.Replay.ID().String(), err) return nil, err } w.l.Info("cleared [%s] [%s] run for replay %s", replayReq.Replay.JobName().String(), runToClear.ScheduledAt, replayReq.Replay.ID().String()) updatedReplayMap := map[time.Time]scheduler.State{ - runToClear.ScheduledAt: scheduler.StateReplayed, + runToClear.ScheduledAt: scheduler.StateInProgress, } updatedRuns := scheduler.JobRunStatusList(replayReq.Runs).MergeWithUpdatedRuns(updatedReplayMap) return updatedRuns, nil @@ -121,27 +172,27 @@ func (w ReplayWorker) processNewReplayRequestSequential(ctx context.Context, rep func (w ReplayWorker) processPartialReplayedRequest(ctx context.Context, replayReq *scheduler.ReplayWithRun, jobCron *cron.ScheduleSpec) error { incomingRuns, err := w.fetchRuns(ctx, replayReq, jobCron) if err != nil { - w.l.Error(fmt.Sprintf("unable to get runs: %s", err.Error()), "replay_id", replayReq.Replay.ID()) + w.l.Error("unable to get runs for replay [%s]: %s", replayReq.Replay.ID().String(), err) return err } updatedReplayMap := identifyUpdatedRunStatus(replayReq.Runs, incomingRuns) updatedRuns := scheduler.JobRunStatusList(replayReq.Runs).MergeWithUpdatedRuns(updatedReplayMap) - replayedRuns := scheduler.JobRunStatusList(updatedRuns).GetSortedRunsByStates([]scheduler.State{scheduler.StateReplayed}) + replayedRuns := scheduler.JobRunStatusList(updatedRuns).GetSortedRunsByStates([]scheduler.State{scheduler.StateInProgress}) toBeReplayedRuns := scheduler.JobRunStatusList(updatedRuns).GetSortedRunsByStates([]scheduler.State{scheduler.StatePending}) replayState := scheduler.ReplayStatePartialReplayed if len(replayedRuns) == 0 && len(toBeReplayedRuns) > 0 { logicalTimeToClear := toBeReplayedRuns[0].GetLogicalTime(jobCron) if err := w.scheduler.Clear(ctx, replayReq.Replay.Tenant(), replayReq.Replay.JobName(), logicalTimeToClear); err != nil { - w.l.Error("unable to clear job run for replay", "replay_id", replayReq.Replay.ID()) + w.l.Error("unable to clear job run for replay_id [%s]: %s", replayReq.Replay.ID().String(), err) return err } w.l.Info("cleared [%s] [%s] run for replay %s", replayReq.Replay.JobName().String(), toBeReplayedRuns[0].ScheduledAt, replayReq.Replay.ID().String()) - updatedReplayMap[toBeReplayedRuns[0].ScheduledAt] = scheduler.StateReplayed - updatedRuns = scheduler.JobRunStatusList(incomingRuns).MergeWithUpdatedRuns(updatedReplayMap) + updatedReplayMap[toBeReplayedRuns[0].ScheduledAt] = scheduler.StateInProgress + updatedRuns = scheduler.JobRunStatusList(updatedRuns).MergeWithUpdatedRuns(updatedReplayMap) } pendingRuns := scheduler.JobRunStatusList(updatedRuns).GetSortedRunsByStates([]scheduler.State{scheduler.StatePending}) @@ -150,22 +201,23 @@ func (w ReplayWorker) processPartialReplayedRequest(ctx context.Context, replayR } if err := w.replayRepo.UpdateReplay(ctx, replayReq.Replay.ID(), replayState, updatedRuns, ""); err != nil { - w.l.Error("unable to update replay state", "replay_id", replayReq.Replay.ID()) + w.l.Error("unable to update replay state for replay_id [%s]: %s", replayReq.Replay.ID().String(), err) return err } + raiseReplayMetric(replayReq.Replay.Tenant(), replayReq.Replay.JobName(), replayState) return nil } func (w ReplayWorker) processReplayedRequest(ctx context.Context, replayReq *scheduler.ReplayWithRun, jobCron *cron.ScheduleSpec) error { incomingRuns, err := w.fetchRuns(ctx, replayReq, jobCron) if err != nil { - w.l.Error(fmt.Sprintf("unable to get runs: %s", err.Error()), "replay_id", replayReq.Replay.ID()) + w.l.Error("unable to get runs for replay with replay_id [%s]: %s", replayReq.Replay.ID().String(), err) return err } updatedReplayMap := identifyUpdatedRunStatus(replayReq.Runs, incomingRuns) - updatedRuns := scheduler.JobRunStatusList(incomingRuns).MergeWithUpdatedRuns(updatedReplayMap) - inProgressRuns := scheduler.JobRunStatusList(updatedRuns).GetSortedRunsByStates([]scheduler.State{scheduler.StateReplayed}) + updatedRuns := scheduler.JobRunStatusList(replayReq.Runs).MergeWithUpdatedRuns(updatedReplayMap) + inProgressRuns := scheduler.JobRunStatusList(updatedRuns).GetSortedRunsByStates([]scheduler.State{scheduler.StateInProgress}) failedRuns := scheduler.JobRunStatusList(updatedRuns).GetSortedRunsByStates([]scheduler.State{scheduler.StateFailed}) var message string @@ -180,9 +232,10 @@ func (w ReplayWorker) processReplayedRequest(ctx context.Context, replayReq *sch } if err := w.replayRepo.UpdateReplay(ctx, replayReq.Replay.ID(), state, updatedRuns, message); err != nil { - w.l.Error("unable to update replay state", "replay_id", replayReq.Replay.ID()) + w.l.Error("unable to update replay with replay_id [%s]: %s", replayReq.Replay.ID().String(), err) return err } + raiseReplayMetric(replayReq.Replay.Tenant(), replayReq.Replay.JobName(), state) return nil } @@ -191,7 +244,7 @@ func identifyUpdatedRunStatus(existingJobRuns, incomingJobRuns []*scheduler.JobR updatedReplayMap := make(map[time.Time]scheduler.State) for _, run := range existingJobRuns { - if run.State != scheduler.StateReplayed { + if run.State != scheduler.StateInProgress { continue } if incomingRunStatusMap[run.ScheduledAt.UTC()] == scheduler.StateSuccess || incomingRunStatusMap[run.ScheduledAt.UTC()] == scheduler.StateFailed { @@ -204,16 +257,18 @@ func identifyUpdatedRunStatus(existingJobRuns, incomingJobRuns []*scheduler.JobR func (w ReplayWorker) getJobCron(ctx context.Context, replayReq *scheduler.ReplayWithRun) (*cron.ScheduleSpec, error) { jobWithDetails, err := w.jobRepo.GetJobDetails(ctx, replayReq.Replay.Tenant().ProjectName(), replayReq.Replay.JobName()) if err != nil || jobWithDetails == nil { - return nil, fmt.Errorf("unable to get job details from DB for jobName: %s, project: %s, error: %w ", - replayReq.Replay.JobName(), replayReq.Replay.Tenant().ProjectName(), err) + return nil, errors.AddErrContext(err, scheduler.EntityReplay, + fmt.Sprintf("unable to get job details for jobName: %s, project: %s", replayReq.Replay.JobName(), replayReq.Replay.Tenant().ProjectName())) } interval := jobWithDetails.Schedule.Interval if interval == "" { - return nil, fmt.Errorf("job schedule interval not found") + w.l.Error("job interval is empty") + return nil, errors.InvalidArgument(scheduler.EntityReplay, "job schedule interval is empty") } jobCron, err := cron.ParseCronSchedule(interval) if err != nil { - return nil, fmt.Errorf("unable to parse job cron interval %w", err) + w.l.Error("error parsing cron interval: %s", err) + return nil, errors.InternalError(scheduler.EntityReplay, "unable to parse job cron interval", err) } return jobCron, nil } @@ -229,6 +284,15 @@ func (w ReplayWorker) fetchRuns(ctx context.Context, replayReq *scheduler.Replay func (w ReplayWorker) updateReplayAsFailed(ctx context.Context, replayID uuid.UUID, message string) { if err := w.replayRepo.UpdateReplayStatus(ctx, replayID, scheduler.ReplayStateFailed, message); err != nil { - w.l.Error("unable to update replay state to failed", "replay_id", replayID) + w.l.Error("unable to update replay state to failed for replay_id [%s]: %s", replayID, err) } } + +func raiseReplayMetric(t tenant.Tenant, jobName scheduler.JobName, state scheduler.ReplayState) { + telemetry.NewCounter(metricJobReplay, map[string]string{ + "project": t.ProjectName().String(), + "namespace": t.NamespaceName().String(), + "job": jobName.String(), + "status": state.String(), + }).Inc() +} diff --git a/core/scheduler/service/replay_worker_test.go b/core/scheduler/service/replay_worker_test.go index bacd7f8b03..5101ba89d7 100644 --- a/core/scheduler/service/replay_worker_test.go +++ b/core/scheduler/service/replay_worker_test.go @@ -7,14 +7,14 @@ import ( "time" "github.com/google/uuid" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/config" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/scheduler/service" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/lib/cron" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/scheduler/service" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/lib/cron" ) func TestReplayWorker(t *testing.T) { @@ -35,6 +35,7 @@ func TestReplayWorker(t *testing.T) { EndDate: endTime, } scheduledTime2 := scheduledTime1.Add(24 * time.Hour) + scheduledTime3 := scheduledTime2.Add(24 * time.Hour) executionTime1 := scheduledTime1.Add(-24 * time.Hour) executionTime2 := executionTime1.Add(24 * time.Hour) jobCronStr := "0 12 * * *" @@ -82,11 +83,12 @@ func TestReplayWorker(t *testing.T) { updatedRuns := []*scheduler.JobRunStatus{ { ScheduledAt: scheduledTime1, - State: scheduler.StateReplayed, + State: scheduler.StateInProgress, }, } jobRepository.On("GetJobDetails", mock.Anything, projName, jobAName).Return(jobAWithDetails, nil) + sch.On("GetJobRuns", mock.Anything, tnnt, mock.Anything, jobCron).Return(replayReq.Runs, nil) sch.On("Clear", mock.Anything, tnnt, jobAName, scheduledTime1.Add(-24*time.Hour)).Return(nil) replayRepository.On("UpdateReplay", mock.Anything, replayReq.Replay.ID(), scheduler.ReplayStateReplayed, updatedRuns, "").Return(nil) @@ -119,7 +121,7 @@ func TestReplayWorker(t *testing.T) { updatedRuns := []*scheduler.JobRunStatus{ { ScheduledAt: scheduledTime1, - State: scheduler.StateReplayed, + State: scheduler.StateInProgress, }, { ScheduledAt: scheduledTime2, @@ -128,6 +130,7 @@ func TestReplayWorker(t *testing.T) { } jobRepository.On("GetJobDetails", mock.Anything, projName, jobAName).Return(jobAWithDetails, nil) + sch.On("GetJobRuns", mock.Anything, tnnt, mock.Anything, jobCron).Return(replayReq.Runs, nil) sch.On("Clear", mock.Anything, tnnt, jobAName, scheduledTime1.Add(-24*time.Hour)).Return(nil) replayRepository.On("UpdateReplay", mock.Anything, replayReq.Replay.ID(), scheduler.ReplayStatePartialReplayed, updatedRuns, "").Return(nil) @@ -159,12 +162,71 @@ func TestReplayWorker(t *testing.T) { } jobRepository.On("GetJobDetails", mock.Anything, projName, jobAName).Return(jobAWithDetails, nil) + sch.On("GetJobRuns", mock.Anything, tnnt, mock.Anything, jobCron).Return(replayReq.Runs, nil) sch.On("ClearBatch", mock.Anything, tnnt, jobAName, executionTime1, executionTime2).Return(nil) replayRepository.On("UpdateReplay", mock.Anything, replayReq.Replay.ID(), scheduler.ReplayStateReplayed, mock.Anything, "").Return(nil) replayWorker := service.NewReplayWorker(logger, replayRepository, sch, jobRepository, replayServerConfig) replayWorker.Process(replayReq) }) + t.Run("should able to process new replay request with creating non existing runs", func(t *testing.T) { + replayRepository := new(ReplayRepository) + defer replayRepository.AssertExpectations(t) + + sch := new(mockReplayScheduler) + defer sch.AssertExpectations(t) + + jobRepository := new(JobRepository) + defer jobRepository.AssertExpectations(t) + + replayReq := &scheduler.ReplayWithRun{ + Replay: scheduler.NewReplay(uuid.New(), jobAName, tnnt, replayConfig, scheduler.ReplayStateCreated, time.Now()), + Runs: []*scheduler.JobRunStatus{ + { + ScheduledAt: scheduledTime1, + State: scheduler.StatePending, + }, + { + ScheduledAt: scheduledTime2, + State: scheduler.StatePending, + }, + }, + } + updatedRunsAfterRunCreate := []*scheduler.JobRunStatus{ + { + ScheduledAt: scheduledTime1, + State: scheduler.StateInProgress, + }, + { + ScheduledAt: scheduledTime2, + State: scheduler.StatePending, + }, + } + updatedRuns := []*scheduler.JobRunStatus{ + { + ScheduledAt: scheduledTime1, + State: scheduler.StateInProgress, + }, + { + ScheduledAt: scheduledTime2, + State: scheduler.StateInProgress, + }, + } + existingRuns := []*scheduler.JobRunStatus{{ + ScheduledAt: scheduledTime2, + State: scheduler.StateSuccess, + }} + + jobRepository.On("GetJobDetails", mock.Anything, projName, jobAName).Return(jobAWithDetails, nil) + sch.On("GetJobRuns", mock.Anything, tnnt, mock.Anything, jobCron).Return(existingRuns, nil) + sch.On("CreateRun", mock.Anything, tnnt, jobAName, scheduledTime1, "replayed").Return(nil).Once() + sch.On("Clear", mock.Anything, tnnt, jobAName, scheduledTime2.Add(-24*time.Hour)).Return(nil) + replayRepository.On("UpdateReplay", mock.Anything, replayReq.Replay.ID(), scheduler.ReplayStatePartialReplayed, updatedRunsAfterRunCreate, "").Return(nil) + replayRepository.On("UpdateReplay", mock.Anything, replayReq.Replay.ID(), scheduler.ReplayStatePartialReplayed, updatedRuns, "").Return(nil) + + replayWorker := service.NewReplayWorker(logger, replayRepository, sch, jobRepository, replayServerConfig) + replayWorker.Process(replayReq) + }) t.Run("should able to update replay state as failed if unable to get job details", func(t *testing.T) { replayRepository := new(ReplayRepository) @@ -221,6 +283,7 @@ func TestReplayWorker(t *testing.T) { } jobRepository.On("GetJobDetails", mock.Anything, projName, jobAName).Return(jobAWithDetails, nil) + sch.On("GetJobRuns", mock.Anything, tnnt, mock.Anything, jobCron).Return(replayReq.Runs, nil) sch.On("ClearBatch", mock.Anything, tnnt, jobAName, executionTime1, executionTime2).Return(internalErr) replayRepository.On("UpdateReplayStatus", mock.Anything, replayReq.Replay.ID(), scheduler.ReplayStateFailed, mock.Anything).Return(nil) @@ -248,6 +311,7 @@ func TestReplayWorker(t *testing.T) { } jobRepository.On("GetJobDetails", mock.Anything, projName, jobAName).Return(jobAWithDetails, nil) + sch.On("GetJobRuns", mock.Anything, tnnt, mock.Anything, jobCron).Return(replayReq.Runs, nil) sch.On("Clear", mock.Anything, tnnt, jobAName, scheduledTime1.Add(-24*time.Hour)).Return(internalErr) replayRepository.On("UpdateReplayStatus", mock.Anything, replayReq.Replay.ID(), scheduler.ReplayStateFailed, mock.Anything).Return(nil) @@ -255,7 +319,7 @@ func TestReplayWorker(t *testing.T) { replayWorker.Process(replayReq) }) - t.Run("should able to process partial replayed request", func(t *testing.T) { + t.Run("should able to process partial replayed request with the recent run status is success", func(t *testing.T) { replayRepository := new(ReplayRepository) defer replayRepository.AssertExpectations(t) @@ -270,12 +334,16 @@ func TestReplayWorker(t *testing.T) { Runs: []*scheduler.JobRunStatus{ { ScheduledAt: scheduledTime1, - State: scheduler.StateReplayed, + State: scheduler.StateInProgress, }, { ScheduledAt: scheduledTime2, State: scheduler.StatePending, }, + { + ScheduledAt: scheduledTime3, + State: scheduler.StatePending, + }, }, } updatedRuns1 := []*scheduler.JobRunStatus{ @@ -285,7 +353,11 @@ func TestReplayWorker(t *testing.T) { }, { ScheduledAt: scheduledTime2, - State: scheduler.StatePending, + State: scheduler.StateFailed, + }, + { + ScheduledAt: scheduledTime3, + State: scheduler.StateFailed, }, } updatedRuns2 := []*scheduler.JobRunStatus{ @@ -295,14 +367,82 @@ func TestReplayWorker(t *testing.T) { }, { ScheduledAt: scheduledTime2, - State: scheduler.StateReplayed, + State: scheduler.StateInProgress, + }, + { + ScheduledAt: scheduledTime3, + State: scheduler.StatePending, + }, + } + + jobRepository.On("GetJobDetails", mock.Anything, projName, jobAName).Return(jobAWithDetails, nil) + sch.On("GetJobRuns", mock.Anything, tnnt, runsCriteriaJobA, jobCron).Return(updatedRuns1, nil).Once() + sch.On("Clear", mock.Anything, tnnt, jobAName, scheduledTime2.Add(-24*time.Hour)).Return(nil) + replayRepository.On("UpdateReplay", mock.Anything, replayReq.Replay.ID(), scheduler.ReplayStatePartialReplayed, updatedRuns2, "").Return(nil).Once() + + replayWorker := service.NewReplayWorker(logger, replayRepository, sch, jobRepository, replayServerConfig) + replayWorker.Process(replayReq) + }) + t.Run("should able to process partial replayed request with the recent run status is failed", func(t *testing.T) { + replayRepository := new(ReplayRepository) + defer replayRepository.AssertExpectations(t) + + sch := new(mockReplayScheduler) + defer sch.AssertExpectations(t) + + jobRepository := new(JobRepository) + defer jobRepository.AssertExpectations(t) + + replayReq := &scheduler.ReplayWithRun{ + Replay: scheduler.NewReplay(uuid.New(), jobAName, tnnt, replayConfigParallel, scheduler.ReplayStatePartialReplayed, time.Now()), + Runs: []*scheduler.JobRunStatus{ + { + ScheduledAt: scheduledTime1, + State: scheduler.StateInProgress, + }, + { + ScheduledAt: scheduledTime2, + State: scheduler.StatePending, + }, + { + ScheduledAt: scheduledTime3, + State: scheduler.StatePending, + }, + }, + } + updatedRuns1 := []*scheduler.JobRunStatus{ + { + ScheduledAt: scheduledTime1, + State: scheduler.StateFailed, + }, + { + ScheduledAt: scheduledTime2, + State: scheduler.StateFailed, + }, + { + ScheduledAt: scheduledTime3, + State: scheduler.StateFailed, + }, + } + updatedRuns2 := []*scheduler.JobRunStatus{ + { + ScheduledAt: scheduledTime1, + State: scheduler.StateFailed, + }, + { + ScheduledAt: scheduledTime2, + State: scheduler.StateInProgress, + }, + { + ScheduledAt: scheduledTime3, + State: scheduler.StatePending, }, } jobRepository.On("GetJobDetails", mock.Anything, projName, jobAName).Return(jobAWithDetails, nil) sch.On("GetJobRuns", mock.Anything, tnnt, runsCriteriaJobA, jobCron).Return(updatedRuns1, nil).Once() sch.On("Clear", mock.Anything, tnnt, jobAName, scheduledTime2.Add(-24*time.Hour)).Return(nil) - replayRepository.On("UpdateReplay", mock.Anything, replayReq.Replay.ID(), scheduler.ReplayStateReplayed, updatedRuns2, "").Return(nil).Once() + replayRepository.On("UpdateReplay", mock.Anything, replayReq.Replay.ID(), scheduler.ReplayStatePartialReplayed, updatedRuns2, "").Return(nil).Once() replayWorker := service.NewReplayWorker(logger, replayRepository, sch, jobRepository, replayServerConfig) replayWorker.Process(replayReq) @@ -322,12 +462,16 @@ func TestReplayWorker(t *testing.T) { Runs: []*scheduler.JobRunStatus{ { ScheduledAt: scheduledTime1, - State: scheduler.StateReplayed, + State: scheduler.StateInProgress, }, { ScheduledAt: scheduledTime2, State: scheduler.StatePending, }, + { + ScheduledAt: scheduledTime3, + State: scheduler.StatePending, + }, }, } @@ -353,7 +497,7 @@ func TestReplayWorker(t *testing.T) { Runs: []*scheduler.JobRunStatus{ { ScheduledAt: scheduledTime1, - State: scheduler.StateReplayed, + State: scheduler.StateInProgress, }, { ScheduledAt: scheduledTime2, @@ -381,7 +525,7 @@ func TestReplayWorker(t *testing.T) { replayWorker.Process(replayReq) }) - t.Run("should able to process replayed request", func(t *testing.T) { + t.Run("should able to process replayed request if all state are success", func(t *testing.T) { replayRepository := new(ReplayRepository) defer replayRepository.AssertExpectations(t) @@ -400,7 +544,7 @@ func TestReplayWorker(t *testing.T) { }, { ScheduledAt: scheduledTime2, - State: scheduler.StateReplayed, + State: scheduler.StateInProgress, }, }, } @@ -422,6 +566,69 @@ func TestReplayWorker(t *testing.T) { replayWorker := service.NewReplayWorker(logger, replayRepository, sch, jobRepository, replayServerConfig) replayWorker.Process(replayReq) }) + t.Run("should able to process replayed request if some of the runs are in failed state", func(t *testing.T) { + replayRepository := new(ReplayRepository) + defer replayRepository.AssertExpectations(t) + + sch := new(mockReplayScheduler) + defer sch.AssertExpectations(t) + + jobRepository := new(JobRepository) + defer jobRepository.AssertExpectations(t) + + replayReq := &scheduler.ReplayWithRun{ + Replay: scheduler.NewReplay(uuid.New(), jobAName, tnnt, replayConfigParallel, scheduler.ReplayStateReplayed, time.Now()), + Runs: []*scheduler.JobRunStatus{ + { + ScheduledAt: scheduledTime1, + State: scheduler.StateInProgress, + }, + { + ScheduledAt: scheduledTime2, + State: scheduler.StateInProgress, + }, + { + ScheduledAt: scheduledTime3, + State: scheduler.StateInProgress, + }, + }, + } + runsFromScheduler := []*scheduler.JobRunStatus{ + { + ScheduledAt: scheduledTime1, + State: scheduler.StateFailed, + }, + { + ScheduledAt: scheduledTime2, + State: scheduler.StateRunning, + }, + { + ScheduledAt: scheduledTime3, + State: scheduler.StateRunning, + }, + } + updatedRuns := []*scheduler.JobRunStatus{ + { + ScheduledAt: scheduledTime1, + State: scheduler.StateFailed, + }, + { + ScheduledAt: scheduledTime2, + State: scheduler.StateInProgress, + }, + { + ScheduledAt: scheduledTime3, + State: scheduler.StateInProgress, + }, + } + + jobRepository.On("GetJobDetails", mock.Anything, projName, jobAName).Return(jobAWithDetails, nil) + sch.On("GetJobRuns", mock.Anything, tnnt, runsCriteriaJobA, jobCron).Return(runsFromScheduler, nil) + replayRepository.On("UpdateReplay", mock.Anything, replayReq.Replay.ID(), scheduler.ReplayStateReplayed, updatedRuns, "").Return(nil) + + replayWorker := service.NewReplayWorker(logger, replayRepository, sch, jobRepository, replayServerConfig) + replayWorker.Process(replayReq) + }) t.Run("should able to update replay state as failed if unable to fetch runs when processing replayed request", func(t *testing.T) { replayRepository := new(ReplayRepository) defer replayRepository.AssertExpectations(t) @@ -441,7 +648,7 @@ func TestReplayWorker(t *testing.T) { }, { ScheduledAt: scheduledTime2, - State: scheduler.StateReplayed, + State: scheduler.StateInProgress, }, }, } @@ -472,7 +679,7 @@ func TestReplayWorker(t *testing.T) { }, { ScheduledAt: scheduledTime2, - State: scheduler.StateReplayed, + State: scheduler.StateInProgress, }, }, } @@ -530,6 +737,20 @@ func (_m *mockReplayScheduler) ClearBatch(ctx context.Context, t tenant.Tenant, return r0 } +// CreateRun provides a mock function with given fields: ctx, tnnt, jobName, executionTime, dagRunID +func (_m *mockReplayScheduler) CreateRun(ctx context.Context, tnnt tenant.Tenant, jobName scheduler.JobName, executionTime time.Time, dagRunIDPrefix string) error { + ret := _m.Called(ctx, tnnt, jobName, executionTime, dagRunIDPrefix) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, tenant.Tenant, scheduler.JobName, time.Time, string) error); ok { + r0 = rf(ctx, tnnt, jobName, executionTime, dagRunIDPrefix) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // GetJobRuns provides a mock function with given fields: ctx, t, criteria, jobCron func (_m *mockReplayScheduler) GetJobRuns(ctx context.Context, t tenant.Tenant, criteria *scheduler.JobRunsCriteria, jobCron *cron.ScheduleSpec) ([]*scheduler.JobRunStatus, error) { ret := _m.Called(ctx, t, criteria, jobCron) diff --git a/core/scheduler/status.go b/core/scheduler/status.go index 8774d033bc..c453114341 100644 --- a/core/scheduler/status.go +++ b/core/scheduler/status.go @@ -5,8 +5,8 @@ import ( "strings" "time" - "github.com/odpf/optimus/internal/errors" - "github.com/odpf/optimus/internal/lib/cron" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/lib/cron" ) const ( @@ -21,8 +21,8 @@ const ( StateSuccess State = "success" StateFailed State = "failed" - // StateReplayed is a replay-specific state to identify a run has been replayed but not yet finished - StateReplayed State = "replayed" + StateWaitUpstream State = "wait_upstream" + StateInProgress State = "in_progress" ) var TaskEndStates = []State{StateSuccess, StateFailed, StateRetry} @@ -45,8 +45,10 @@ func StateFromString(state string) (State, error) { return StateSuccess, nil case string(StateFailed): return StateFailed, nil - case string(StateReplayed): - return StateReplayed, nil + case string(StateWaitUpstream): + return StateWaitUpstream, nil + case string(StateInProgress): + return StateInProgress, nil default: return "", errors.InvalidArgument(EntityJobRun, "invalid state for run "+state) } @@ -97,6 +99,14 @@ func (j JobRunStatusList) GetSortedRunsByStates(states []State) []*JobRunStatus return result } +func (j JobRunStatusList) GetSortedRunsByScheduledAt() []*JobRunStatus { + result := []*JobRunStatus(j) + sort.Slice(result, func(i, j int) bool { + return result[i].ScheduledAt.Before(result[j].ScheduledAt) + }) + return result +} + func (j JobRunStatusList) MergeWithUpdatedRuns(updatedRunMap map[time.Time]State) []*JobRunStatus { var updatedRuns []*JobRunStatus for _, run := range j { diff --git a/core/scheduler/status_test.go b/core/scheduler/status_test.go index 5d167e562f..3f812e4b98 100644 --- a/core/scheduler/status_test.go +++ b/core/scheduler/status_test.go @@ -6,8 +6,8 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/internal/lib/cron" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/internal/lib/cron" ) func TestStatus(t *testing.T) { @@ -86,20 +86,20 @@ func TestStatus(t *testing.T) { }) t.Run("StateFromString", func(t *testing.T) { expectationsMap := map[string]scheduler.State{ - "pending": scheduler.StatePending, - "PENDING": scheduler.StatePending, - "accepted": scheduler.StateAccepted, - "ACCEPTED": scheduler.StateAccepted, - "running": scheduler.StateRunning, - "RUNNING": scheduler.StateRunning, - "queued": scheduler.StateQueued, - "QUEUED": scheduler.StateQueued, - "success": scheduler.StateSuccess, - "SUCCESS": scheduler.StateSuccess, - "failed": scheduler.StateFailed, - "FAILED": scheduler.StateFailed, - "replayed": scheduler.StateReplayed, - "REPLAYED": scheduler.StateReplayed, + "pending": scheduler.StatePending, + "PENDING": scheduler.StatePending, + "accepted": scheduler.StateAccepted, + "ACCEPTED": scheduler.StateAccepted, + "running": scheduler.StateRunning, + "RUNNING": scheduler.StateRunning, + "queued": scheduler.StateQueued, + "QUEUED": scheduler.StateQueued, + "success": scheduler.StateSuccess, + "SUCCESS": scheduler.StateSuccess, + "failed": scheduler.StateFailed, + "FAILED": scheduler.StateFailed, + "in_progress": scheduler.StateInProgress, + "IN_PROGRESS": scheduler.StateInProgress, } for input, expectedState := range expectationsMap { respState, err := scheduler.StateFromString(input) @@ -157,6 +157,43 @@ func TestStatus(t *testing.T) { runs := jobRunStatusList.GetSortedRunsByStates([]scheduler.State{scheduler.StateRunning}) assert.Equal(t, expectedRuns, runs) }) + t.Run("GetSortedRunsByScheduledAt", func(t *testing.T) { + time1 := time.Date(2023, 0o1, 1, 0, 0, 0, 0, time.UTC) + time2 := time.Date(2023, 0o1, 2, 0, 0, 0, 0, time.UTC) + time3 := time.Date(2023, 0o1, 3, 0, 0, 0, 0, time.UTC) + + jobRunStatusList := scheduler.JobRunStatusList([]*scheduler.JobRunStatus{ + { + ScheduledAt: time3, + State: scheduler.StateRunning, + }, + { + ScheduledAt: time1, + State: scheduler.StatePending, + }, + { + ScheduledAt: time2, + State: scheduler.StateRunning, + }, + }) + expectedRuns := []*scheduler.JobRunStatus{ + { + ScheduledAt: time1, + State: scheduler.StatePending, + }, + { + ScheduledAt: time2, + State: scheduler.StateRunning, + }, + { + ScheduledAt: time3, + State: scheduler.StateRunning, + }, + } + + runs := jobRunStatusList.GetSortedRunsByScheduledAt() + assert.Equal(t, expectedRuns, runs) + }) t.Run("MergeWithUpdatedRuns", func(t *testing.T) { time1 := time.Date(2023, 0o1, 1, 0, 0, 0, 0, time.UTC) time2 := time.Date(2023, 0o1, 2, 0, 0, 0, 0, time.UTC) diff --git a/core/tenant/handler/v1beta1/namespace.go b/core/tenant/handler/v1beta1/namespace.go index 27202043be..9633142fe4 100644 --- a/core/tenant/handler/v1beta1/namespace.go +++ b/core/tenant/handler/v1beta1/namespace.go @@ -4,11 +4,11 @@ import ( "context" "strings" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) type NamespaceService interface { @@ -29,16 +29,19 @@ func (nh *NamespaceHandler) RegisterProjectNamespace(ctx context.Context, req *p ) { projName, err := tenant.ProjectNameFrom(req.GetProjectName()) if err != nil { + nh.l.Error("error adapting project name [%s]: %s", req.GetProjectName(), err) return nil, errors.GRPCErr(err, "error in register namespace "+req.GetNamespace().Name) } namespace, err := fromNamespaceProto(req.GetNamespace(), projName) if err != nil { + nh.l.Error("error adapting project [%s]: %s", projName, err) return nil, errors.GRPCErr(err, "error in register namespace "+req.GetNamespace().Name) } err = nh.nsService.Save(ctx, namespace) if err != nil { + nh.l.Error("error saving namespace: %s", err) return nil, errors.GRPCErr(err, "error in register namespace "+req.GetNamespace().Name) } @@ -50,11 +53,13 @@ func (nh *NamespaceHandler) ListProjectNamespaces(ctx context.Context, req *pb.L ) { projName, err := tenant.ProjectNameFrom(req.GetProjectName()) if err != nil { + nh.l.Error("error adapting project name [%s]: %s", req.GetProjectName(), err) return nil, errors.GRPCErr(err, "error in list namespaces") } namespaces, err := nh.nsService.GetAll(ctx, projName) if err != nil { + nh.l.Error("error getting all namespaces for project [%s]: %s", projName, err) return nil, errors.GRPCErr(err, "error in list namespaces") } @@ -73,16 +78,19 @@ func (nh *NamespaceHandler) GetNamespace(ctx context.Context, request *pb.GetNam ) { projName, err := tenant.ProjectNameFrom(request.GetProjectName()) if err != nil { + nh.l.Error("error adapting project name [%s]: %s", request.GetProjectName(), err) return nil, errors.GRPCErr(err, "error in get namespace "+request.NamespaceName) } namespaceName, err := tenant.NamespaceNameFrom(request.GetNamespaceName()) if err != nil { + nh.l.Error("error adapting namespace name [%s]: %s", request.GetNamespaceName(), err) return nil, errors.GRPCErr(err, "error in get namespace "+request.NamespaceName) } namespace, err := nh.nsService.Get(ctx, projName, namespaceName) if err != nil { + nh.l.Error("error getting namespace: %s", err) return nil, errors.GRPCErr(err, "error in get namespace "+request.NamespaceName) } diff --git a/core/tenant/handler/v1beta1/namespace_test.go b/core/tenant/handler/v1beta1/namespace_test.go index d75426d515..f56221f3af 100644 --- a/core/tenant/handler/v1beta1/namespace_test.go +++ b/core/tenant/handler/v1beta1/namespace_test.go @@ -5,13 +5,13 @@ import ( "errors" "testing" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/core/tenant/handler/v1beta1" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/core/tenant/handler/v1beta1" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) func TestNamespaceHandler(t *testing.T) { diff --git a/core/tenant/handler/v1beta1/project.go b/core/tenant/handler/v1beta1/project.go index 2995646fb1..a94b29b171 100644 --- a/core/tenant/handler/v1beta1/project.go +++ b/core/tenant/handler/v1beta1/project.go @@ -5,11 +5,11 @@ import ( "fmt" "strings" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) type ProjectHandler struct { @@ -34,9 +34,11 @@ type TenantService interface { func (ph *ProjectHandler) RegisterProject(ctx context.Context, req *pb.RegisterProjectRequest) (*pb.RegisterProjectResponse, error) { project, err := fromProjectProto(req.GetProject()) if err != nil { + ph.l.Error("error adapting project: %s", err) return nil, errors.GRPCErr(err, fmt.Sprintf("not able to register project %s", req.GetProject().Name)) } if err := ph.projectService.Save(ctx, project); err != nil { + ph.l.Error("error saving project: %s", err) return nil, errors.GRPCErr(err, fmt.Sprintf("not able to register project %s", req.GetProject().Name)) } @@ -47,6 +49,7 @@ func (ph *ProjectHandler) RegisterProject(ctx context.Context, req *pb.RegisterP func (ph *ProjectHandler) ListProjects(ctx context.Context, _ *pb.ListProjectsRequest) (*pb.ListProjectsResponse, error) { projects, err := ph.projectService.GetAll(ctx) if err != nil { + ph.l.Error("error getting all projects: %s", err) return nil, errors.GRPCErr(err, "failed to retrieve saved projects") } @@ -63,10 +66,12 @@ func (ph *ProjectHandler) ListProjects(ctx context.Context, _ *pb.ListProjectsRe func (ph *ProjectHandler) GetProject(ctx context.Context, req *pb.GetProjectRequest) (*pb.GetProjectResponse, error) { projName, err := tenant.ProjectNameFrom(req.GetProjectName()) if err != nil { + ph.l.Error("error adapting project name [%s]: %s", req.GetProjectName(), err) return nil, errors.GRPCErr(err, fmt.Sprintf("failed to retrieve project [%s]", req.GetProjectName())) } project, err := ph.projectService.Get(ctx, projName) if err != nil { + ph.l.Error("error getting project [%s]: %s", projName, err) return nil, errors.GRPCErr(err, fmt.Sprintf("failed to retrieve project [%s]", req.GetProjectName())) } return &pb.GetProjectResponse{ diff --git a/core/tenant/handler/v1beta1/project_test.go b/core/tenant/handler/v1beta1/project_test.go index 3c7079b00d..7079d58e10 100644 --- a/core/tenant/handler/v1beta1/project_test.go +++ b/core/tenant/handler/v1beta1/project_test.go @@ -5,13 +5,13 @@ import ( "errors" "testing" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/core/tenant/handler/v1beta1" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/core/tenant/handler/v1beta1" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) func TestProjectHandler(t *testing.T) { diff --git a/core/tenant/handler/v1beta1/secret.go b/core/tenant/handler/v1beta1/secret.go index 5518f86465..787b576ac7 100644 --- a/core/tenant/handler/v1beta1/secret.go +++ b/core/tenant/handler/v1beta1/secret.go @@ -4,13 +4,24 @@ import ( "context" "encoding/base64" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "google.golang.org/protobuf/types/known/timestamppb" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/core/tenant/dto" - "github.com/odpf/optimus/internal/errors" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/core/tenant/dto" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/telemetry" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" +) + +const ( + metricSecretEvents = "secret_events_total" + secretEventsStatusRegistered = "registered" + secretEventsStatusUpdated = "updated" + secretEventsStatusDeleted = "deleted" + secretEventsStatusRegisterFailed = "register_failed" + secretEventsStatusUpdateFailed = "update_failed" + secretEventsStatusDeleteFailed = "delete_failed" ) type SecretService interface { @@ -35,18 +46,22 @@ func (sv *SecretHandler) RegisterSecret(ctx context.Context, req *pb.RegisterSec base64Decoded, err := getDecodedSecret(req.GetValue()) if err != nil { + raiseSecretEventsMetric(projName.String(), req.NamespaceName, secretEventsStatusRegisterFailed) return nil, errors.GRPCErr(err, "failed to register secret "+req.GetSecretName()) } secret, err := tenant.NewPlainTextSecret(req.GetSecretName(), base64Decoded) if err != nil { + raiseSecretEventsMetric(projName.String(), req.NamespaceName, secretEventsStatusRegisterFailed) return nil, errors.GRPCErr(err, "failed to register secret "+req.GetSecretName()) } if err = sv.secretService.Save(ctx, projName, req.GetNamespaceName(), secret); err != nil { + raiseSecretEventsMetric(projName.String(), req.NamespaceName, secretEventsStatusRegisterFailed) return nil, errors.GRPCErr(err, "failed to register secret "+req.GetSecretName()) } + raiseSecretEventsMetric(projName.String(), req.NamespaceName, secretEventsStatusRegistered) return &pb.RegisterSecretResponse{}, nil } @@ -58,18 +73,22 @@ func (sv *SecretHandler) UpdateSecret(ctx context.Context, req *pb.UpdateSecretR base64Decoded, err := getDecodedSecret(req.GetValue()) if err != nil { + raiseSecretEventsMetric(projName.String(), req.NamespaceName, secretEventsStatusUpdateFailed) return nil, errors.GRPCErr(err, "failed to update secret "+req.GetSecretName()) } secret, err := tenant.NewPlainTextSecret(req.GetSecretName(), base64Decoded) if err != nil { + raiseSecretEventsMetric(projName.String(), req.NamespaceName, secretEventsStatusUpdateFailed) return nil, errors.GRPCErr(err, "failed to update secret "+req.GetSecretName()) } if err = sv.secretService.Update(ctx, projName, req.GetNamespaceName(), secret); err != nil { + raiseSecretEventsMetric(projName.String(), req.NamespaceName, secretEventsStatusUpdateFailed) return nil, errors.GRPCErr(err, "failed to update secret "+req.GetSecretName()) } + raiseSecretEventsMetric(projName.String(), req.NamespaceName, secretEventsStatusUpdated) return &pb.UpdateSecretResponse{}, nil } @@ -110,9 +129,11 @@ func (sv *SecretHandler) DeleteSecret(ctx context.Context, req *pb.DeleteSecretR } if err = sv.secretService.Delete(ctx, projName, req.GetNamespaceName(), secretName); err != nil { + raiseSecretEventsMetric(projName.String(), req.NamespaceName, secretEventsStatusDeleteFailed) return nil, errors.GRPCErr(err, "failed to delete secret "+secretName.String()) } + raiseSecretEventsMetric(projName.String(), req.NamespaceName, secretEventsStatusDeleted) return &pb.DeleteSecretResponse{}, nil } @@ -134,3 +155,11 @@ func NewSecretsHandler(l log.Logger, secretService SecretService) *SecretHandler secretService: secretService, } } + +func raiseSecretEventsMetric(projectName, namespaceName, state string) { + telemetry.NewCounter(metricSecretEvents, map[string]string{ + "project": projectName, + "namespace": namespaceName, + "status": state, + }).Inc() +} diff --git a/core/tenant/handler/v1beta1/secret_test.go b/core/tenant/handler/v1beta1/secret_test.go index 0928652c99..57dcfcb44a 100644 --- a/core/tenant/handler/v1beta1/secret_test.go +++ b/core/tenant/handler/v1beta1/secret_test.go @@ -7,14 +7,14 @@ import ( "testing" "time" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/core/tenant/dto" - "github.com/odpf/optimus/core/tenant/handler/v1beta1" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/core/tenant/dto" + "github.com/raystack/optimus/core/tenant/handler/v1beta1" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) func TestNewSecretsHandler(t *testing.T) { diff --git a/core/tenant/namespace.go b/core/tenant/namespace.go index 5fd2855063..0239eaf027 100644 --- a/core/tenant/namespace.go +++ b/core/tenant/namespace.go @@ -1,7 +1,7 @@ package tenant import ( - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/internal/errors" ) const EntityNamespace = "namespace" diff --git a/core/tenant/namespace_test.go b/core/tenant/namespace_test.go index 441e92785a..0313668061 100644 --- a/core/tenant/namespace_test.go +++ b/core/tenant/namespace_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/tenant" + "github.com/raystack/optimus/core/tenant" ) func TestEntityNamespace(t *testing.T) { diff --git a/core/tenant/project.go b/core/tenant/project.go index c52fd6f639..2f04de3db7 100644 --- a/core/tenant/project.go +++ b/core/tenant/project.go @@ -1,8 +1,8 @@ package tenant import ( - "github.com/odpf/optimus/internal/errors" - "github.com/odpf/optimus/internal/utils" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/utils" ) const ( diff --git a/core/tenant/project_test.go b/core/tenant/project_test.go index 194b474af4..22c49048ce 100644 --- a/core/tenant/project_test.go +++ b/core/tenant/project_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/tenant" + "github.com/raystack/optimus/core/tenant" ) func TestEntityProject(t *testing.T) { diff --git a/core/tenant/secret.go b/core/tenant/secret.go index 784f5c72f2..22dcb4f92e 100644 --- a/core/tenant/secret.go +++ b/core/tenant/secret.go @@ -1,6 +1,6 @@ package tenant -import "github.com/odpf/optimus/internal/errors" +import "github.com/raystack/optimus/internal/errors" const ( EntitySecret = "secret" diff --git a/core/tenant/secret_test.go b/core/tenant/secret_test.go index e61637a8e1..cf11e45cd5 100644 --- a/core/tenant/secret_test.go +++ b/core/tenant/secret_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/tenant" + "github.com/raystack/optimus/core/tenant" ) func TestEntitySecret(t *testing.T) { diff --git a/core/tenant/service/namespace_service.go b/core/tenant/service/namespace_service.go index 38e9092b7c..00e9c2f9f5 100644 --- a/core/tenant/service/namespace_service.go +++ b/core/tenant/service/namespace_service.go @@ -3,7 +3,7 @@ package service import ( "context" - "github.com/odpf/optimus/core/tenant" + "github.com/raystack/optimus/core/tenant" ) type NamespaceRepository interface { diff --git a/core/tenant/service/namespace_service_test.go b/core/tenant/service/namespace_service_test.go index ac060ab093..0b83a09b62 100644 --- a/core/tenant/service/namespace_service_test.go +++ b/core/tenant/service/namespace_service_test.go @@ -8,8 +8,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/core/tenant/service" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/core/tenant/service" ) func TestNamespaceService(t *testing.T) { diff --git a/core/tenant/service/project_service.go b/core/tenant/service/project_service.go index fa2be5149d..9c8570cb7b 100644 --- a/core/tenant/service/project_service.go +++ b/core/tenant/service/project_service.go @@ -3,7 +3,7 @@ package service import ( "context" - "github.com/odpf/optimus/core/tenant" + "github.com/raystack/optimus/core/tenant" ) type ProjectService struct { diff --git a/core/tenant/service/project_service_test.go b/core/tenant/service/project_service_test.go index 16abe5ad24..da763cb293 100644 --- a/core/tenant/service/project_service_test.go +++ b/core/tenant/service/project_service_test.go @@ -8,8 +8,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/core/tenant/service" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/core/tenant/service" ) func TestProjectService(t *testing.T) { diff --git a/core/tenant/service/secret_service.go b/core/tenant/service/secret_service.go index 993d908855..e62c89597c 100644 --- a/core/tenant/service/secret_service.go +++ b/core/tenant/service/secret_service.go @@ -4,10 +4,11 @@ import ( "context" "github.com/gtank/cryptopasta" + "github.com/raystack/salt/log" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/core/tenant/dto" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/core/tenant/dto" + "github.com/raystack/optimus/internal/errors" ) const keyLength = 32 @@ -24,20 +25,25 @@ type SecretRepository interface { type SecretService struct { appKey *[keyLength]byte repo SecretRepository + + logger log.Logger } func (s SecretService) Save(ctx context.Context, projName tenant.ProjectName, nsName string, secret *tenant.PlainTextSecret) error { if secret == nil { + s.logger.Error("secret is nil") return errors.InvalidArgument(tenant.EntitySecret, "secret is not valid") } encoded, err := cryptopasta.Encrypt([]byte(secret.Value()), s.appKey) if err != nil { + s.logger.Error("error encrypting secret: %s", err) return errors.InternalError(tenant.EntitySecret, "unable to encrypt the secret", err) } item, err := tenant.NewSecret(secret.Name().String(), string(encoded), projName, nsName) if err != nil { + s.logger.Error("error encountered when constructing a new secret: %s", err) return err } @@ -46,16 +52,19 @@ func (s SecretService) Save(ctx context.Context, projName tenant.ProjectName, ns func (s SecretService) Update(ctx context.Context, projName tenant.ProjectName, nsName string, secret *tenant.PlainTextSecret) error { if secret == nil { + s.logger.Error("secret is nil") return errors.InvalidArgument(tenant.EntitySecret, "secret is not valid") } encoded, err := cryptopasta.Encrypt([]byte(secret.Value()), s.appKey) if err != nil { + s.logger.Error("error encrypting secret: %s", err) return errors.InternalError(tenant.EntitySecret, "unable to encrypt the secret", err) } item, err := tenant.NewSecret(secret.Name().String(), string(encoded), projName, nsName) if err != nil { + s.logger.Error("error constructing a new secret: %s", err) return err } @@ -65,20 +74,24 @@ func (s SecretService) Update(ctx context.Context, projName tenant.ProjectName, func (s SecretService) Get(ctx context.Context, projName tenant.ProjectName, namespaceName, name string) (*tenant.PlainTextSecret, error) { secretName, err := tenant.SecretNameFrom(name) if err != nil { + s.logger.Error("error adapting secret name [%s]: %s", name, err) return nil, errors.InvalidArgument(tenant.EntitySecret, "secret name is not valid") } if projName == "" { + s.logger.Error("project name for secret [%s] is empty") return nil, errors.InvalidArgument(tenant.EntitySecret, "tenant is not valid") } secret, err := s.repo.Get(ctx, projName, namespaceName, secretName) if err != nil { + s.logger.Error("error getting stored secret: %s", err) return nil, err } cleartext, err := cryptopasta.Decrypt([]byte(secret.EncodedValue()), s.appKey) if err != nil { + s.logger.Error("error decrypting secret: %s", err) return nil, err } @@ -87,11 +100,13 @@ func (s SecretService) Get(ctx context.Context, projName tenant.ProjectName, nam func (s SecretService) GetAll(ctx context.Context, projName tenant.ProjectName, namespaceName string) ([]*tenant.PlainTextSecret, error) { if projName == "" { + s.logger.Error("project name is empty") return nil, errors.InvalidArgument(tenant.EntitySecret, "project name is not valid") } secrets, err := s.repo.GetAll(ctx, projName, namespaceName) if err != nil { + s.logger.Error("error getting all secrets under project [%s] namespace [%s]: %s", projName, namespaceName, err) return nil, err } @@ -99,11 +114,13 @@ func (s SecretService) GetAll(ctx context.Context, projName tenant.ProjectName, for i, secret := range secrets { cleartext, err := cryptopasta.Decrypt([]byte(secret.EncodedValue()), s.appKey) if err != nil { + s.logger.Error("error decrypting secret [%s]: %s", secret.Name().String(), err) return nil, err } pts, err := tenant.NewPlainTextSecret(secret.Name().String(), string(cleartext)) if err != nil { + s.logger.Error("error constructing plain text secret: %s", err) return nil, err } ptsecrets[i] = pts @@ -114,6 +131,7 @@ func (s SecretService) GetAll(ctx context.Context, projName tenant.ProjectName, func (s SecretService) Delete(ctx context.Context, projName tenant.ProjectName, nsName string, name tenant.SecretName) error { if name == "" { + s.logger.Error("secret name is empty") return errors.InvalidArgument(tenant.EntitySecret, "secret name is not valid") } @@ -124,9 +142,10 @@ func (s SecretService) GetSecretsInfo(ctx context.Context, projName tenant.Proje return s.repo.GetSecretsInfo(ctx, projName) } -func NewSecretService(appKey *[32]byte, repo SecretRepository) *SecretService { +func NewSecretService(appKey *[32]byte, repo SecretRepository, logger log.Logger) *SecretService { return &SecretService{ appKey: appKey, repo: repo, + logger: logger, } } diff --git a/core/tenant/service/secret_service_test.go b/core/tenant/service/secret_service_test.go index 2ef4b1ff51..8745ae4720 100644 --- a/core/tenant/service/secret_service_test.go +++ b/core/tenant/service/secret_service_test.go @@ -5,12 +5,13 @@ import ( "errors" "testing" + "github.com/raystack/salt/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/core/tenant/dto" - "github.com/odpf/optimus/core/tenant/service" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/core/tenant/dto" + "github.com/raystack/optimus/core/tenant/service" ) func TestSecretService(t *testing.T) { @@ -21,11 +22,13 @@ func TestSecretService(t *testing.T) { nsName := "test-namespace" invalidSecret := tenant.Secret{} + logger := log.NewLogrus() + t.Run("Save", func(t *testing.T) { t.Run("returns error when secret is not provided", func(t *testing.T) { secretRepo := new(secretRepo) - secretService := service.NewSecretService(key, secretRepo) + secretService := service.NewSecretService(key, secretRepo, logger) err := secretService.Save(ctx, projectName, nsName, nil) assert.NotNil(t, err) assert.EqualError(t, err, "invalid argument for entity secret: secret is not valid") @@ -34,7 +37,7 @@ func TestSecretService(t *testing.T) { secretRepo := new(secretRepo) invalidSecret := tenant.PlainTextSecret{} - secretService := service.NewSecretService(key, secretRepo) + secretService := service.NewSecretService(key, secretRepo, logger) err := secretService.Save(ctx, projectName, nsName, &invalidSecret) assert.NotNil(t, err) assert.EqualError(t, err, "invalid argument for entity secret: secret name is empty") @@ -47,7 +50,7 @@ func TestSecretService(t *testing.T) { sec, err := tenant.NewPlainTextSecret("name", "value") assert.Nil(t, err) - secretService := service.NewSecretService(key, secretRepo) + secretService := service.NewSecretService(key, secretRepo, logger) err = secretService.Save(ctx, projectName, nsName, sec) assert.NotNil(t, err) assert.EqualError(t, err, "error in save") @@ -60,7 +63,7 @@ func TestSecretService(t *testing.T) { sec, err := tenant.NewPlainTextSecret("name", "value") assert.Nil(t, err) - secretService := service.NewSecretService(key, secretRepo) + secretService := service.NewSecretService(key, secretRepo, logger) err = secretService.Save(ctx, projectName, nsName, sec) assert.Nil(t, err) }) @@ -69,7 +72,7 @@ func TestSecretService(t *testing.T) { t.Run("returns error when secret is not provided", func(t *testing.T) { secretRepo := new(secretRepo) - secretService := service.NewSecretService(key, secretRepo) + secretService := service.NewSecretService(key, secretRepo, logger) err := secretService.Update(ctx, projectName, nsName, nil) assert.NotNil(t, err) assert.EqualError(t, err, "invalid argument for entity secret: secret is not valid") @@ -78,7 +81,7 @@ func TestSecretService(t *testing.T) { secretRepo := new(secretRepo) invalidSecret := tenant.PlainTextSecret{} - secretService := service.NewSecretService(key, secretRepo) + secretService := service.NewSecretService(key, secretRepo, logger) err := secretService.Update(ctx, projectName, nsName, &invalidSecret) assert.NotNil(t, err) assert.EqualError(t, err, "invalid argument for entity secret: secret name is empty") @@ -91,7 +94,7 @@ func TestSecretService(t *testing.T) { sec, err := tenant.NewPlainTextSecret("name", "value") assert.Nil(t, err) - secretService := service.NewSecretService(key, secretRepo) + secretService := service.NewSecretService(key, secretRepo, logger) err = secretService.Update(ctx, projectName, nsName, sec) assert.NotNil(t, err) assert.EqualError(t, err, "error in update") @@ -104,7 +107,7 @@ func TestSecretService(t *testing.T) { sec, err := tenant.NewPlainTextSecret("name", "value") assert.Nil(t, err) - secretService := service.NewSecretService(key, secretRepo) + secretService := service.NewSecretService(key, secretRepo, logger) err = secretService.Update(ctx, projectName, nsName, sec) assert.Nil(t, err) }) @@ -113,7 +116,7 @@ func TestSecretService(t *testing.T) { t.Run("returns error when secret name is empty", func(t *testing.T) { secretRepo := new(secretRepo) - secretService := service.NewSecretService(key, secretRepo) + secretService := service.NewSecretService(key, secretRepo, logger) _, err := secretService.Get(ctx, projectName, nsName, "") assert.NotNil(t, err) assert.EqualError(t, err, "invalid argument for entity secret: secret name is not valid") @@ -121,7 +124,7 @@ func TestSecretService(t *testing.T) { t.Run("returns error when project name is invalid", func(t *testing.T) { secretRepo := new(secretRepo) - secretService := service.NewSecretService(key, secretRepo) + secretService := service.NewSecretService(key, secretRepo, logger) _, err := secretService.Get(ctx, "", nsName, "name") assert.NotNil(t, err) assert.EqualError(t, err, "invalid argument for entity secret: tenant is not valid") @@ -134,7 +137,7 @@ func TestSecretService(t *testing.T) { secretRepo.On("Get", ctx, projectName, nsName, sn).Return(nil, errors.New("error in get")) defer secretRepo.AssertExpectations(t) - secretService := service.NewSecretService(key, secretRepo) + secretService := service.NewSecretService(key, secretRepo, logger) _, err = secretService.Get(ctx, projectName, nsName, "name") assert.NotNil(t, err) assert.EqualError(t, err, "error in get") @@ -147,7 +150,7 @@ func TestSecretService(t *testing.T) { secretRepo.On("Get", ctx, projectName, nsName, sn).Return(&invalidSecret, nil) defer secretRepo.AssertExpectations(t) - secretService := service.NewSecretService(key, secretRepo) + secretService := service.NewSecretService(key, secretRepo, logger) _, err = secretService.Get(ctx, projectName, nsName, "name") assert.NotNil(t, err) assert.EqualError(t, err, "malformed ciphertext") @@ -167,7 +170,7 @@ func TestSecretService(t *testing.T) { secretRepo.On("Get", ctx, projectName, nsName, sn).Return(sec, nil) defer secretRepo.AssertExpectations(t) - secretService := service.NewSecretService(key, secretRepo) + secretService := service.NewSecretService(key, secretRepo, logger) s, err := secretService.Get(ctx, projectName, nsName, "name") assert.Nil(t, err) assert.Equal(t, "name", s.Name().String()) @@ -178,7 +181,7 @@ func TestSecretService(t *testing.T) { t.Run("returns error when project name is invalid", func(t *testing.T) { secretRepo := new(secretRepo) - secretService := service.NewSecretService(key, secretRepo) + secretService := service.NewSecretService(key, secretRepo, logger) _, err := secretService.GetAll(ctx, "", "") assert.NotNil(t, err) assert.EqualError(t, err, "invalid argument for entity secret: project name is not valid") @@ -188,7 +191,7 @@ func TestSecretService(t *testing.T) { secretRepo.On("GetAll", ctx, projectName, nsName).Return(nil, errors.New("error in get all")) defer secretRepo.AssertExpectations(t) - secretService := service.NewSecretService(key, secretRepo) + secretService := service.NewSecretService(key, secretRepo, logger) _, err := secretService.GetAll(ctx, projectName, nsName) assert.NotNil(t, err) assert.EqualError(t, err, "error in get all") @@ -199,7 +202,7 @@ func TestSecretService(t *testing.T) { Return([]*tenant.Secret{&invalidSecret}, nil) defer secretRepo.AssertExpectations(t) - secretService := service.NewSecretService(key, secretRepo) + secretService := service.NewSecretService(key, secretRepo, logger) _, err := secretService.GetAll(ctx, projectName, nsName) assert.NotNil(t, err) assert.EqualError(t, err, "malformed ciphertext") @@ -214,7 +217,7 @@ func TestSecretService(t *testing.T) { secretRepo.On("GetAll", ctx, projectName, nsName).Return([]*tenant.Secret{sec}, nil) defer secretRepo.AssertExpectations(t) - secretService := service.NewSecretService(key, secretRepo) + secretService := service.NewSecretService(key, secretRepo, logger) s, err := secretService.GetAll(ctx, projectName, nsName) assert.Nil(t, err) assert.Equal(t, "name", s[0].Name().String()) @@ -225,7 +228,7 @@ func TestSecretService(t *testing.T) { t.Run("returns error when secret name is empty", func(t *testing.T) { secretRepo := new(secretRepo) - secretService := service.NewSecretService(key, secretRepo) + secretService := service.NewSecretService(key, secretRepo, logger) err := secretService.Delete(ctx, projectName, nsName, "") assert.NotNil(t, err) assert.EqualError(t, err, "invalid argument for entity secret: secret name is not valid") @@ -238,7 +241,7 @@ func TestSecretService(t *testing.T) { secretRepo.On("Delete", ctx, projectName, nsName, sn).Return(errors.New("error in delete")) defer secretRepo.AssertExpectations(t) - secretService := service.NewSecretService(key, secretRepo) + secretService := service.NewSecretService(key, secretRepo, logger) err = secretService.Delete(ctx, projectName, nsName, sn) assert.NotNil(t, err) assert.EqualError(t, err, "error in delete") @@ -251,7 +254,7 @@ func TestSecretService(t *testing.T) { secretRepo.On("Delete", ctx, projectName, nsName, sn).Return(nil) defer secretRepo.AssertExpectations(t) - secretService := service.NewSecretService(key, secretRepo) + secretService := service.NewSecretService(key, secretRepo, logger) err = secretService.Delete(ctx, projectName, nsName, "name") assert.Nil(t, err) }) @@ -267,7 +270,7 @@ func TestSecretService(t *testing.T) { secretRepo.On("GetSecretsInfo", ctx, projectName).Return([]*dto.SecretInfo{&secretInfo}, nil) defer secretRepo.AssertExpectations(t) - secretService := service.NewSecretService(key, secretRepo) + secretService := service.NewSecretService(key, secretRepo, logger) info, err := secretService.GetSecretsInfo(ctx, projectName) assert.Nil(t, err) assert.Equal(t, 1, len(info)) diff --git a/core/tenant/service/tenant_service.go b/core/tenant/service/tenant_service.go index 23a8931118..794087bb0d 100644 --- a/core/tenant/service/tenant_service.go +++ b/core/tenant/service/tenant_service.go @@ -3,8 +3,10 @@ package service import ( "context" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/salt/log" + + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" ) type ProjectGetter interface { @@ -24,25 +26,31 @@ type TenantService struct { projGetter ProjectGetter namespaceGetter NamespaceGetter secretsGetter SecretsGetter + + logger log.Logger } func (t TenantService) GetDetails(ctx context.Context, tnnt tenant.Tenant) (*tenant.WithDetails, error) { if tnnt.IsInvalid() { + t.logger.Error("tenant information is invalid") return nil, errors.InvalidArgument(tenant.EntityTenant, "invalid tenant details provided") } proj, err := t.projGetter.Get(ctx, tnnt.ProjectName()) if err != nil { + t.logger.Error("error getting project [%s]: %s", tnnt.ProjectName().String(), err) return nil, err } namespace, err := t.namespaceGetter.Get(ctx, tnnt.ProjectName(), tnnt.NamespaceName()) if err != nil { + t.logger.Error("error getting namespace [%s]: %s", tnnt.NamespaceName().String(), err) return nil, err } secrets, err := t.secretsGetter.GetAll(ctx, tnnt.ProjectName(), tnnt.NamespaceName().String()) if err != nil { + t.logger.Error("error getting all secrets for project [%s] namespace [%s]: %s", tnnt.ProjectName(), tnnt.NamespaceName(), err) return nil, err } @@ -51,6 +59,7 @@ func (t TenantService) GetDetails(ctx context.Context, tnnt tenant.Tenant) (*ten func (t TenantService) GetProject(ctx context.Context, name tenant.ProjectName) (*tenant.Project, error) { if name == "" { + t.logger.Error("project name is empty") return nil, errors.InvalidArgument(tenant.EntityTenant, "invalid project name") } return t.projGetter.Get(ctx, name) @@ -58,6 +67,7 @@ func (t TenantService) GetProject(ctx context.Context, name tenant.ProjectName) func (t TenantService) GetSecrets(ctx context.Context, tnnt tenant.Tenant) ([]*tenant.PlainTextSecret, error) { if tnnt.IsInvalid() { + t.logger.Error("tenant information is invalid") return nil, errors.InvalidArgument(tenant.EntityTenant, "tenant is invalid") } return t.secretsGetter.GetAll(ctx, tnnt.ProjectName(), tnnt.NamespaceName().String()) @@ -65,15 +75,17 @@ func (t TenantService) GetSecrets(ctx context.Context, tnnt tenant.Tenant) ([]*t func (t TenantService) GetSecret(ctx context.Context, tnnt tenant.Tenant, name string) (*tenant.PlainTextSecret, error) { if tnnt.IsInvalid() { + t.logger.Error("tenant information is invalid") return nil, errors.InvalidArgument(tenant.EntityTenant, "tenant is invalid") } return t.secretsGetter.Get(ctx, tnnt.ProjectName(), tnnt.NamespaceName().String(), name) } -func NewTenantService(projGetter ProjectGetter, nsGetter NamespaceGetter, secretsGetter SecretsGetter) *TenantService { +func NewTenantService(projGetter ProjectGetter, nsGetter NamespaceGetter, secretsGetter SecretsGetter, logger log.Logger) *TenantService { return &TenantService{ projGetter: projGetter, namespaceGetter: nsGetter, secretsGetter: secretsGetter, + logger: logger, } } diff --git a/core/tenant/service/tenant_service_test.go b/core/tenant/service/tenant_service_test.go index 21d980e6c2..e888422bdc 100644 --- a/core/tenant/service/tenant_service_test.go +++ b/core/tenant/service/tenant_service_test.go @@ -5,11 +5,12 @@ import ( "errors" "testing" + "github.com/raystack/salt/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/core/tenant/service" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/core/tenant/service" ) func TestTenantService(t *testing.T) { @@ -23,9 +24,11 @@ func TestTenantService(t *testing.T) { ns, _ := tenant.NewNamespace("testNS", proj.Name(), map[string]string{}) tnnt, _ := tenant.NewTenant(proj.Name().String(), ns.Name().String()) + logger := log.NewLogrus() + t.Run("GetDetails", func(t *testing.T) { t.Run("returns error when tenant invalid", func(t *testing.T) { - tenantService := service.NewTenantService(nil, nil, nil) + tenantService := service.NewTenantService(nil, nil, nil, logger) _, err := tenantService.GetDetails(ctx, tenant.Tenant{}) assert.NotNil(t, err) @@ -36,7 +39,7 @@ func TestTenantService(t *testing.T) { projGetter.On("Get", ctx, tnnt.ProjectName()).Return(nil, errors.New("unable to get")) defer projGetter.AssertExpectations(t) - tenantService := service.NewTenantService(projGetter, nil, nil) + tenantService := service.NewTenantService(projGetter, nil, nil, logger) _, err := tenantService.GetDetails(ctx, tnnt) assert.NotNil(t, err) @@ -51,7 +54,7 @@ func TestTenantService(t *testing.T) { nsGetter.On("Get", ctx, tnnt.ProjectName(), tnnt.NamespaceName()).Return(nil, errors.New("unable to get ns")) defer nsGetter.AssertExpectations(t) - tenantService := service.NewTenantService(projGetter, nsGetter, nil) + tenantService := service.NewTenantService(projGetter, nsGetter, nil, logger) _, err := tenantService.GetDetails(ctx, tnnt) assert.NotNil(t, err) @@ -70,7 +73,7 @@ func TestTenantService(t *testing.T) { secGetter.On("GetAll", ctx, tnnt.ProjectName(), tnnt.NamespaceName().String()).Return(nil, errors.New("unable to get secrets")) defer secGetter.AssertExpectations(t) - tenantService := service.NewTenantService(projGetter, nsGetter, secGetter) + tenantService := service.NewTenantService(projGetter, nsGetter, secGetter, logger) _, err := tenantService.GetDetails(ctx, tnnt) assert.NotNil(t, err) @@ -91,7 +94,7 @@ func TestTenantService(t *testing.T) { Return([]*tenant.PlainTextSecret{pts}, nil) defer secGetter.AssertExpectations(t) - tenantService := service.NewTenantService(projGetter, nsGetter, secGetter) + tenantService := service.NewTenantService(projGetter, nsGetter, secGetter, logger) d, err := tenantService.GetDetails(ctx, tnnt) assert.Nil(t, err) @@ -107,7 +110,7 @@ func TestTenantService(t *testing.T) { t.Run("returns error when project name is invalid", func(t *testing.T) { projGetter := new(projectGetter) - tenantService := service.NewTenantService(projGetter, nil, nil) + tenantService := service.NewTenantService(projGetter, nil, nil, logger) _, err := tenantService.GetProject(ctx, "") assert.NotNil(t, err) @@ -118,7 +121,7 @@ func TestTenantService(t *testing.T) { projGetter.On("Get", ctx, tnnt.ProjectName()).Return(proj, nil) defer projGetter.AssertExpectations(t) - tenantService := service.NewTenantService(projGetter, nil, nil) + tenantService := service.NewTenantService(projGetter, nil, nil, logger) p, err := tenantService.GetProject(ctx, tnnt.ProjectName()) assert.Nil(t, err) @@ -129,7 +132,7 @@ func TestTenantService(t *testing.T) { t.Run("GetSecrets", func(t *testing.T) { t.Run("returns error when project name is invalid", func(t *testing.T) { secretsGetter := new(secretGetter) - tenantService := service.NewTenantService(nil, nil, secretsGetter) + tenantService := service.NewTenantService(nil, nil, secretsGetter, logger) invalidTenant := tenant.Tenant{} _, err := tenantService.GetSecrets(ctx, invalidTenant) @@ -142,7 +145,7 @@ func TestTenantService(t *testing.T) { secretsGetter.On("GetAll", ctx, proj.Name(), ns.Name().String()).Return([]*tenant.PlainTextSecret{pts}, nil) defer secretsGetter.AssertExpectations(t) - tenantService := service.NewTenantService(nil, nil, secretsGetter) + tenantService := service.NewTenantService(nil, nil, secretsGetter, logger) secrets, err := tenantService.GetSecrets(ctx, tnnt) assert.Nil(t, err) @@ -152,7 +155,7 @@ func TestTenantService(t *testing.T) { t.Run("GetSecret", func(t *testing.T) { t.Run("return error when project name is invalid", func(t *testing.T) { secretsGetter := new(secretGetter) - tenantService := service.NewTenantService(nil, nil, secretsGetter) + tenantService := service.NewTenantService(nil, nil, secretsGetter, logger) invalidTenant := tenant.Tenant{} _, err := tenantService.GetSecret(ctx, invalidTenant, "secret") @@ -165,7 +168,7 @@ func TestTenantService(t *testing.T) { secretsGetter.On("Get", ctx, proj.Name(), ns.Name().String(), "secret_name").Return(pts, nil) defer secretsGetter.AssertExpectations(t) - tenantService := service.NewTenantService(nil, nil, secretsGetter) + tenantService := service.NewTenantService(nil, nil, secretsGetter, logger) secret, err := tenantService.GetSecret(ctx, tnnt, pts.Name().String()) assert.Nil(t, err) diff --git a/core/tenant/tenant.go b/core/tenant/tenant.go index 079ecacf48..84b563c59e 100644 --- a/core/tenant/tenant.go +++ b/core/tenant/tenant.go @@ -1,8 +1,8 @@ package tenant import ( - "github.com/odpf/optimus/internal/errors" - "github.com/odpf/optimus/internal/utils" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/utils" ) const EntityTenant = "tenant" diff --git a/core/tenant/tenant_test.go b/core/tenant/tenant_test.go index 349d4cff79..72480f27a1 100644 --- a/core/tenant/tenant_test.go +++ b/core/tenant/tenant_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/tenant" + "github.com/raystack/optimus/core/tenant" ) func TestAggregateRootTenant(t *testing.T) { diff --git a/dev/Makefile b/dev/Makefile index 3ec61bb5e1..5a775d2a38 100644 --- a/dev/Makefile +++ b/dev/Makefile @@ -1,6 +1,6 @@ EXPECTED_CONTEXT=colima NAMESPACE=optimus-dev -OPTIMUS_ODPF_CHART=odpf/optimus +OPTIMUS_raystack_CHART=raystack/optimus OPTIMUS_PLUGINS_PATH=/tmp/colima/plugins DAGS_PATH?=/tmp/colima/dags @@ -21,9 +21,9 @@ apply.airflow: _context apply.optimus: _context mkdir -p ${DAGS_PATH} mkdir -p ${OPTIMUS_PLUGINS_PATH} - helm repo add odpf https://odpf.github.io/charts/ + helm repo add raystack https://raystack.github.io/charts/ $(eval OPTIMUS_PLUGIN_ARTIFACTS = $(shell ./get_artifacts.sh ${SETUP_FILE_PATH})) - helm upgrade --install optimus-dev ${OPTIMUS_ODPF_CHART} \ + helm upgrade --install optimus-dev ${OPTIMUS_raystack_CHART} \ -n ${NAMESPACE} \ --set volumes[0].hostPath.path=$(abspath ${DAGS_PATH}) \ --set volumes[1].hostPath.path=$(abspath ${OPTIMUS_PLUGINS_PATH}) \ diff --git a/dev/setup.yaml b/dev/setup.yaml index 43870b6d07..68bd5240e3 100644 --- a/dev/setup.yaml +++ b/dev/setup.yaml @@ -1,5 +1,5 @@ plugins: - - https://raw.githubusercontent.com/odpf/transformers/main/task/bq2bq/optimus-plugin-bq2bq.yaml + - https://raw.githubusercontent.com/raystack/transformers/main/task/bq2bq/optimus-plugin-bq2bq.yaml - ./plugins/optimus-plugin-echo.yaml secrets: - name: BQ_SERVICE_ACCOUNT diff --git a/docs/blog/2021-10-02-optimus-launch.md b/docs/blog/2021-10-02-optimus-launch.md index 59d210df72..7ed1d531dd 100644 --- a/docs/blog/2021-10-02-optimus-launch.md +++ b/docs/blog/2021-10-02-optimus-launch.md @@ -5,7 +5,7 @@ authors: name: Ravi Suhag title: Maintainer url: https://github.com/ravisuhag -tags: [odpf, optimus] +tags: [raystack, optimus] --- We are live! diff --git a/docs/docs/building-plugin/introduction.md b/docs/docs/building-plugin/introduction.md new file mode 100644 index 0000000000..cc43830049 --- /dev/null +++ b/docs/docs/building-plugin/introduction.md @@ -0,0 +1,99 @@ +# Introduction of Plugin Development + +As mentioned in the [concepts](../concepts/plugin.md), plugins provide support for various data warehouses & any +third party system to handle all the data transformations or movement. Before we start, let’s take a look at different +implementations of plugin: YAML and binary. + +## Yaml Implementation of Plugin +Most plugins are expected to implement just the info and project side use-cases (mentioned above) and these are +data-driven i.e., plugin just provide data to Optimus. To simplify the development process of plugins, support for +yaml mode of defining plugins is added. + +```go +// representation of a yaml plugin schema in golang + +// below struct definition in golang can be marshalled +// to generate yaml plugins + +type YamlPlugin struct { +// info use-case +Name string `yaml:"name"` +Description string `yaml:"description"` +Plugintype string `yaml:"plugintype"` +Pluginversion string `yaml:"pluginversion"` +Image string `yaml:"image"` + + // survey use-case + Questions []struct { + Name string `yaml:"name"` + Prompt string `yaml:"prompt"` + Help string `yaml:"help"` + Regexp string `yaml:"regexp"` + Validationerror string `yaml:"validationerror"` + Minlength int `yaml:"minlength"` + Required bool `yaml:"required,omitempty"` + Maxlength int `yaml:"maxlength,omitempty"` + Subquestions []struct { + Ifvalue string `yaml:"ifvalue"` + Questions []struct { + Name string `yaml:"name"` + Prompt string `yaml:"prompt"` + Help string `yaml:"help"` + Multiselect []string `yaml:"multiselect"` + Regexp string `yaml:"regexp"` + Validationerror string `yaml:"validationerror"` + Minlength int `yaml:"minlength"` + Required bool `yaml:"required,omitempty"` + Maxlength int `yaml:"maxlength,omitempty"` + } `yaml:"questions"` + } `yaml:"subquestions,omitempty"` + } `yaml:"questions"` + + // default-static-values use-case + Defaultassets []struct { + Name string `yaml:"name"` + Value string `yaml:"value"` + } `yaml:"defaultassets"` + Defaultconfig []struct { + Name string `yaml:"name"` + Value string `yaml:"value"` + } `yaml:"defaultconfig"` +} +``` + +Refer to sample implementation here. + + +### Limitations of Yaml plugins: +Here the scope of YAML plugins is limited to driving surveys, providing default values for job config and assets, and +providing plugin info. As the majority of the plugins are expected to implement a subset of these use cases, the +support for YAML definitions for plugins is added which simplifies the development, packaging, and distribution of plugins. + +For plugins that require enriching Optimus server-side behavior, YAML definitions fall short as this would require some code. + +### Validating Yaml plugins: +Also support for validating yaml plugin is added into optimus. After creating yaml definitions of plugin, one can +validate them as below: + +```shell +optimus plugin validate --path {{directory of yaml plugins}} +``` + +** Note: The yaml plugin is expected to have file name as optimus-plugin-{{name}}.yaml +** Note: If Both yaml and binary plugin with same name are installed, Yaml implementation is prioritized over the +corresponding counterparts in binary implementation. + +## Binary Implementation of Plugin (to be deprecated) +Binary implementations of Plugins are binaries which implement predefined protobuf interfaces to extend Optimus +functionalities and augment the yaml implementations with executable code. Binary Plugins are implemented using +go-plugin developed by Hashicorp used in terraform and other similar products. Currently, Dependency Resolution Mod +is the only interface that is supported in the binary approach to plugins. + +Note : Binary plugins augment yaml plugins and they are not standalone. + +Plugins can be implemented in any language as long as they can be exported as a single self-contained executable binary +and implements a GRPC server. It is recommended to use Go currently for writing plugins because of its cross platform +build functionality and to reuse protobuf sdk provided within Optimus core. + +_Binary Plugins can potentially modify the behavior of Optimus in undesired ways. Exercise caution when adding new +plugins developed by unrecognized developers._ diff --git a/docs/docs/building-plugin/tutorial.md b/docs/docs/building-plugin/tutorial.md new file mode 100644 index 0000000000..93486c775b --- /dev/null +++ b/docs/docs/building-plugin/tutorial.md @@ -0,0 +1,258 @@ +# Tutorial of Plugin Development +To demonstrate the previous-mentioned wrapping functionality, let's create a plugin in Go as well as a yaml definition +and use python for actual transformation logic. You can choose to fork this [example](https://github.com/kushsharma/optimus-plugins) +template and modify it as per your needs or start fresh. To demonstrate how to start from scratch, we will be starting +from an empty git repository and build a plugin which will find potential hazardous **Near Earth Orbit** objects every +day, let's call it **neo** for short. + +Brief description of Neo is as follows +- Using NASA [API](https://api.nasa.gov/) we can get count of hazardous objects, their diameter and velocity. +- Task will need two config as input, RANGE_START, RANGE_END as date time string which will filter the count for + this specific period only. +- Execute every day, say at 2 AM. +- Need a secret token that will be passed to NASA api endpoint for each request. +- Output of this object count can be printed in logs for now but in a real use case can be pushed to Kafka topic or + written to a database. +- Plugin will be written in **YAML** format and Neo in **python**. + +## Preparing task executor +Start by initialising an empty git repository with the following folder structure +``` +.git +/task + /neo + /executor + /main.py + /requirements.txt + /Dockerfile +README.md +``` + +That is three folders one inside another. This might look confusing for now, a lot of things will, but just keep going. +Create an empty python file in executor main.py, this is where the main logic for interacting with nasa api and +generating output will be. For simplicity, lets use as minimal things as possible. + +Add the following code to main.py + +```python +import os +import requests +import json + +def start(): + """ + Sends a http call to nasa api, parses response and prints potential hazardous + objects in near earth orbit + :return: + """ + opt_config = fetch_config_from_optimus() + + # user configuration for date range + range_start = opt_config["envs"]["RANGE_START"] + range_end = opt_config["envs"]["RANGE_END"] + + secret_key = os.environ["SECRET_KEY"] + + # secret token required for NASA API being passed using job spec + api_key = json.loads(secret_key) + if api_key is None: + raise Exception("invalid api token") + + # send the request for given date range + r = requests.get(url="https://api.nasa.gov/neo/rest/v1/feed", + params={'start_date': range_start, 'end_date': range_end, 'api_key': api_key}) + + # extracting data in json format + print("for date range {} - {}".format(range_start, range_end)) + print_details(r.json()) + + return + + +def fetch_config_from_optimus(): + """ + Fetch configuration inputs required to run this task for a single schedule day + Configurations are fetched using optimus rest api + :return: + """ + # try printing os env to see what all we have for debugging + # print(os.environ) + + # prepare request + optimus_host = os.environ["OPTIMUS_HOSTNAME"] + scheduled_at = os.environ["SCHEDULED_AT"] + project_name = os.environ["PROJECT"] + job_name = os.environ["JOB_NAME"] + + r = requests.post(url="http://{}/api/v1/project/{}/job/{}/instance".format(optimus_host, project_name, job_name), + json={'scheduled_at': scheduled_at, + 'instance_name': "neo", + 'instance_type': "TASK"}) + instance = r.json() + + # print(instance) + return instance["context"] + + + +if __name__ == "__main__": + start() + +``` + +**api_key** is a token provided by nasa during registration. This token will be passed as a parameter in each http call. +**SECRET_PATH** is the path to a file which will contain this token in json and will be mounted inside the docker +container by Optimus. + +**start** function is using **fetch_config_from_optimus()** to get the date range for which this task executes for +an iteration. In this example, configuration is fetched using REST APIs provided by optimus although there are variety +of ways to get them. After extracting **API_KEY** from secret file, unmarshalling it to json with **json.load()** +send a http request to nasa api. Response can be parsed and printed using the following function: + +```python +def print_details(jd): + """ + Parse and calculate what we need from NASA endpoint response + + :param jd: json data fetched from NASA API + :return: + """ + element_count = jd['element_count'] + potentially_hazardous = [] + for search_date in jd['near_earth_objects'].keys(): + for neo in jd['near_earth_objects'][search_date]: + if neo["is_potentially_hazardous_asteroid"] is True: + potentially_hazardous.append({ + "name": neo["name"], + "estimated_diameter_km": neo["estimated_diameter"]["kilometers"]["estimated_diameter_max"], + "relative_velocity_kmh": neo["close_approach_data"][0]["relative_velocity"]["kilometers_per_hour"] + }) + + print("total tracking: {}\npotential hazardous: {}".format(element_count, len(potentially_hazardous))) + for haz in potentially_hazardous: + print("Name: {}\nEstimated Diameter: {} km\nRelative Velocity: {} km/h\n\n".format( + haz["name"], + haz["estimated_diameter_km"], + haz["relative_velocity_kmh"] + )) + return + +``` + +Finish it off by adding the main function + +```python +if __name__ == "__main__": +start() +``` + +Add requests library in requirements.txt +``` +requests==v2.25.1 +``` + +Once the python code is ready, wrap this in a Dockerfile + +```dockerfile +# set base image (host OS) +FROM python:3.8 + +# set the working directory in the container +RUN mkdir -p /opt +WORKDIR /opt + +# copy the content of the local src directory to the working directory +COPY task/neo/executor . + +# install dependencies +RUN pip install -r requirements.txt + +CMD ["python3", "main.py"] + +``` + + +## Creating a yaml plugin +The Yaml implementation is as simple as providing the plugin details as below. + +```yaml +name: Neo +description: Near earth object tracker +plugintype: task +pluginversion: latest +image: ghcr.io/kushsharma/optimus-task-neo-executor +secretpath: /tmp/.secrets + +questions: +- name: RANGE_START + prompt: Date range start + help: YYYY-MM-DD format + required: true +- name: RANGE_END + prompt: Date range end + help: YYYY-MM-DD format + required: true +``` + +Based on the usecase, additional validation can be added to the questions section. + +This yaml plugin can be placed anywhere, however for this tutorial let’s place it under `../task/neo` directory and +name it as `optimus-plugin-neo.yaml`. + +Note: As part of this tutorial, we are not providing binary plugin tutorial as it is going to be deprecated. + +## How to Use + +Before using, let’s install this new plugin in server and client. + +### Installing the plugin in server +To use the created plugin in your server, you can simpy add the plugin path in the server config: + +```yaml +plugin: + artifacts: + - ../task/neo/optimus-plugin-neo.yaml +``` + +To apply the change, you can follow either of these options: +- Start Optimus server using `--install-plugins` flag, or +- Install the plugin separately before starting the server using `optimus plugin install` command. + +_Note: Take a look at installing plugins in server [guide](../server-guide/installing-plugins.md) for more information._ + +### Installing the plugin in client +Install the plugin in client side by syncing it from server using below command. +```shell +$ optimus plugin sync +```` + +Once finished, the `Neo` plugin will be available in the `.plugins` directory. + +### Use the plugin in job creation + +Once everything is built and in place, we can generate job specifications that uses neo as the task type. + +```shell +optimus create job +? What is the job name? is_last_day_on_earth +? Who is the owner of this job? owner@example.io +? Which task to run? neo +? Specify the start date 2022-01-25 +? Specify the interval (in crontab notation) 0 2 * * * +? Transformation window daily +? Date range start {{.DSTART}} +? Date range end {{.DEND}} +job created successfully is_last_day_on_earth +``` + +Create a commit and deploy this specification if you are using optimus with a git managed repositories or send +a REST call or use GRPC, whatever floats your boat. + +Once the job has been deployed and run, open the task log and verify something like this +``` +total tracking: 14 +potential hazardous: 1 +Name: (2014 KP4) +Estimated Diameter: 0.8204270649 km +Relative Velocity: 147052.9914506647 km/h +``` diff --git a/docs/docs/client-guide/applying-job-specifications.md b/docs/docs/client-guide/applying-job-specifications.md new file mode 100644 index 0000000000..3541a1a93a --- /dev/null +++ b/docs/docs/client-guide/applying-job-specifications.md @@ -0,0 +1,43 @@ +# Applying Job Specifications +Once you have the job specifications ready, let’s try to deploy the jobs to the server by running this command: + +```shell +$ optimus job replace-all --verbose +``` +Note: add --config flag if you are not in the same directory with your client configuration (optimus.yaml). + +This replace-all command works per project or namespace level and will try to compare the incoming jobs and the jobs +in the server. You will find in the logs how many jobs are new, modified, and deleted based on the current condition. + +```shell +$ optimus job replace-all --verbose + +> Validating namespaces +validation finished! + +> Replacing all jobs for namespaces [sample_namespace] +> Receiving responses: +[sample_namespace] received 1 job specs +[sample_namespace] found 1 new, 0 modified, and 0 deleted job specs +[sample_namespace] processing job job1 +[sample_namespace] successfully added 1 jobs +replace all job specifications finished! +``` + + +You might notice based on the log that Optimus tries to find which jobs are new, modified, or deleted. This is because +Optimus will not try to process every job in every single `replace-all` command for performance reasons. If you have +needs to refresh all of the jobs in the project from the server, regardless it has changed or not, do run the below command: + +```shell +$ optimus job refresh --verbose +``` + +This refresh command is not taking any specifications as a request. It will only refresh the jobs in the server. + +Also, do notice that these **replace-all** and **refresh** commands are only for registering the job specifications in the server, +including resolving the dependencies. After this, you can compile and upload the jobs to the scheduler using the +`scheduler upload-all` [command](uploading-jobs-to-scheduler.md). + +Note: Currently Optimus does not provide a way to deploy only a single job through CLI. This capability is being +supported in the API. diff --git a/docs/docs/client-guide/backup-bigquery-resource.md b/docs/docs/client-guide/backup-bigquery-resource.md new file mode 100644 index 0000000000..4f02fe5781 --- /dev/null +++ b/docs/docs/client-guide/backup-bigquery-resource.md @@ -0,0 +1,43 @@ +# Backup BigQuery Resource + +Backup is a common prerequisite step to be done before re-running or modifying a resource. Currently, Optimus supports +backup for BigQuery tables and provides dependency resolution, so backup can be also done to all the downstream tables +as long as it is registered in Optimus and within the same project. + +## Configuring backup details +Several configurations can be set to have the backup result in your project as your preference. Here are the available +configurations for BigQuery datastore. + +| Configuration Key | Description | Default | +|-------------------|------------------------------------------|-----------------| +| ttl | Time to live in duration | 720h | +| prefix | Prefix of the result table name | backup | +| dataset | Where the table result should be located | optimus_backup | + +_Note: these values can be set in the project configuration._ + +## Run a backup +To start a backup, run the following command: +```shell +$ optimus backup create --resource "resource_name" --project sample-project --namespace sample-namespace +``` + + +After you run the command, prompts will be shown. You will need to answer the questions. +```shell +$ optimus backup create --resource "resource_name" --project sample-project --namespace sample-namespace +? Select supported datastore? bigquery +? Why is this backup needed? backfill due to business logic change +``` + +Once the backup is finished, the backup results along with where it is located will be shown. + +## Get the list of backups +List of recent backups of a project can be checked using this subcommand: +```shell +$ optimus backup list --project sample-project +``` + +Recent backup ID including the resource, when it was created, what is the description or purpose of the backup will +be shown. The backup ID is used as a postfix in the backup result name, thus you can find those results in the datastore +(for example BigQuery) using the backup ID. However, keep in mind that these backup results have an expiry time set. diff --git a/docs/docs/client-guide/configuration.md b/docs/docs/client-guide/configuration.md new file mode 100644 index 0000000000..951625548f --- /dev/null +++ b/docs/docs/client-guide/configuration.md @@ -0,0 +1,73 @@ +# Configuration +Client configuration holds the necessary information for connecting to the Optimus server as well as for specification +creation. Optimus provides a way for you to initialize the client configuration by using the `init` command. Go to the +directory where you want to have your Optimus specifications. Run the below command and answer the prompt questions: + +```shell +$ optimus init + +? What is the Optimus service host? localhost:9100 +? What is the Optimus project name? sample_project +? What is the namespace name? sample_namespace +? What is the type of data store for this namespace? bigquery +? Do you want to add another namespace? No +Client config is initialized successfully +``` + + +After running the init command, the Optimus client config will be configured. Along with it, the directories for the +chosen namespaces, including the sub-directories for jobs and resources will be created with the following structure: +``` +sample_project +├── sample_namespace +│ └── jobs +│ └── resources +└── optimus.yaml +``` + +Below is the client configuration that has been generated: +```yaml +version: 1 +log: + level: INFO + format: "" +host: localhost:9100 +project: + name: sample_project + config: {} +namespaces: +- name: sample_namespace + config: {} + job: + path: sample_namespace/jobs + datastore: + - type: bigquery + path: sample_namespace/resources + backup: {} +``` + + +| Configuration | Description | +|----------------|-------------------------------------------------| +| Version | Supports only version 1 at the moment. | +| Log | Logging level & format configuration | +| Host | Optimus server host | +| Project | Chosen Optimus project name and configurations. | +| Namespaces | Namespaces that are owned by the project. | + +## Project +- Project name should be unique. +- Several configs are mandatory for job compilation and deployment use case: + - **storage_path** config to store the job compilation result. A path can be anything, for example, a local directory + path or a Google Cloud Storage path. + - **scheduler_host** being used for job execution and sensors. + - Specific secrets might be needed for the above configs. Take a look at the detail [here](managing-secrets.md). +- You can put any other project configurations which can be used in job specifications. + +## Namespaces +- Name should be unique in the project. +- You can put any namespace configurations which can be used in specifications. +- Job path needs to be properly set so Optimus CLI will able to find all of your job specifications to be processed. +- For datastore, currently Optimus only accepts `bigquery` datastore type and you need to set the specification path + for this. Also, there is an optional `backup` config map. Take a look at the backup guide section [here](backup-bigquery-resource.md) + to understand more about this. diff --git a/docs/docs/client-guide/create-job-specifications.md b/docs/docs/client-guide/create-job-specifications.md new file mode 100644 index 0000000000..6000dd997a --- /dev/null +++ b/docs/docs/client-guide/create-job-specifications.md @@ -0,0 +1,242 @@ +# Create Job Specifications + +A Job is the fundamental execution unit of an Optimus data pipeline. It can be scheduled, configured and is always +mapped to a single transformation type (eg, BQ-to-BQ, GCS-to-BQ, etc). It can have dependencies over other jobs and +should only execute once the dependent job is successfully completed. + +A job can also be configured with Hooks as part of its lifecycle, which can be triggered before or after the job. +Please go through the [concept](../concepts/job.md) to know more about it. + +Before we begin, let’s understand the flow of job creation & deployment (later) in Optimus. + +![Create Job Flow](/img/docs/CreateJobSpecFlow.png "CreateJobSpecFlow") + +For this guide, we'll be creating a job that writes "hello YYYY-MM-DD" to a table every day at 03.00 AM. We'll use the +BQ-to-BQ transformation type. For the purpose of this guide, we'll assume that the Google Cloud Project name is +"sample-project" & dataset is just called "playground". + +## Initialize Job Specification + +Open your terminal and create a new directory that will hold the specifications created by Optimus CLI. Once ready, +you can run the following command and answer the corresponding prompts (do note that some prompts would be to select +from options instead of input): + +```shell +$ optimus job create +? Please choose the namespace: sample_namespace +? Provide new directory name to create for this spec? [.] sample-project.playground.table1 +? What is the job name? sample-project.playground.table1 +? Who is the owner of this job? sample_owner +? Select task to run? bq2bq +? Specify the schedule start date 2023-01-26 +? Specify the schedule interval (in crontab notation) 0 2 * * * +? Transformation window daily +? Project ID sample-project +? Dataset Name playground +? Table ID table1 +? Load method to use on destination REPLACE +Job successfully created at sample-project.playground.table1 +``` + +After running the job create command, the job specification file and assets directory are created in the following directory. + +``` +├── sample_namespace +│ └── jobs +| └── sample-project.playground.table1 +| └── assets +| └── query.sql +| └── job.yaml +│ └── resources +└── optimus.yaml +``` + +Do notice that query.sql file is also generated. This is because, for BQ to BQ job, transformation logic lies in the +query.sql file. We will update this file based on the requirement later. + +For now, let’s take a deeper look at the job.yaml that Optimus has generated and understands what it does. After +taking a look at the possible configurations, we will try to complete the transformation task and take a look at how +to add a hook. + +```yaml +version: 1 +name: sample-project.playground.table1 +owner: sample_owner +schedule: + start_date: "2023-01-26" + interval: 0 2 * * * +behavior: + depends_on_past: false +task: + name: bq2bq + config: + DATASET: playground + LOAD_METHOD: REPLACE + PROJECT: sample-project + SQL_TYPE: STANDARD + TABLE: table1 +window: + size: 24h + offset: "0" + truncate_to: d +labels: + orchestrator: optimus +hooks: [] +dependencies: [] +``` + +## Understanding the Job Specifications + +| Job Configuration | Description | +| ----------------- | -------------------------------------------------------------------------------------------------------- | +| Version | Version 1 and 2 (recommended) are available. This affects the windowing capability. | +| Name | Should be unique in the project. | +| Owner | Owner of the job, can be an email, team name, slack handle, or anything that works for your team. | +| Schedule | Specifications needed to schedule a job, such as start_date, end_date and interval (cron) | +| Behavior | Specifications that represents how the scheduled jobs should behave, for example when the run is failed. | +| Task | Specifications related to the transformation task | +| Hooks | Name & configuration of pre/post hooks. Take a look at how to add hooks [here](#adding-hook). | +| Labels | Help you to identify your job. Any of the values will also be marked as a tag in Airflow. | +| Dependencies | Represent the list of jobs that are considered upstream. | +| Metadata | Represents additional resource and scheduler configurations. | + +### Behavior + +Behavior specification might consist: + +- depends_on_past: set to true to not allow the task to run, if the previous task run has not been succeeded yet +- retry + - count: represents how many times it will try to retrigger the job if the job failed to run + - delay + - exponential_backoff +- notify: Alert configurations. Take a look more at this [here](setting-up-alert.md). + +### Task + +Task specification might consist: + +- name +- config: Some configs might be needed for a specific task type. For example, for BQ to BQ task, it is required to have + BQ_SERVICE_ACCOUNT, PROJECT, DATASET, TABLE, SQL_TYPE, LOAD_METHOD configs. Take a look at the details of what is load method here. +- window: Take a look at the details of the window [here](../concepts/intervals-and-windows.md). + +### Dependencies + +Represent the list of jobs that are considered upstream. + +- If the job is in a different project, include the Optimus’ project name in the prefix. +- If the job is in the same project, simply mentioning the job name is sufficient. + +Example: + +```yaml +dependencies: + - job: sample-project.playground.table1 + - job: other-project/other-project.playground.table2 +``` + +### Metadata + +Below specifications can be set in Metadata section: + +- **resource**: set up CPU/memory request/limit +- **airflow**: set up which Airflow pool and what is the queue configuration for this job + +## Completing the Transformation Task + +Let’s retake a look at the generated task specifications + +```yaml +task: + name: bq2bq + config: + DATASET: playground + LOAD_METHOD: REPLACE + PROJECT: sample-project + SQL_TYPE: STANDARD + TABLE: table1 +window: + size: 24h + offset: "0" + truncate_to: d +``` + +Here are the details of each configuration and the allowed values: + +| Config Name | Description | Values | +| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | +| PROJECT | GCP project ID of the destination BigQuery table | | +| DATASET | BigQuery dataset name of the destination table | | +| TABLE | the table name of the destination table | | +| LOAD_METHOD | method to load data to the destination tables | Take a detailed look [here](https://github.com/raystack/transformers/blob/main/task/bq2bq/README.md) | +| PARTITION_FILTER | Used to identify target partitions to replace in a REPLACE query. This can be left empty and Optimus will figure out the target partitions automatically but it's cheaper and faster to specify the condition. This filter will be used as a where clause in a merge statement to delete the partitions from the destination table. | event_timestamp >= "{{.DSTART}}" AND event_timestamp < "{{.DEND}}" | + +Now let's try to modify the core transformation logic that lies in `assets/query.sql`. Remember that we are going to +create a job that writes "hello YYYY-MM-DD" to a table every day at 03.00 AM. Now, let’s modify the query so it prints +what we intended: + +```sql +SELECT CONCAT("Hello, ", "{{.DEND}}") AS message; +``` + +{{.DEND}} is a macro that is replaced with the current execution date (in YYYY-MM-DD format) of the task ( +note that this is the execution date of when the task was supposed to run, not when it actually runs). Take a detailed +look at the supported macros [here](../concepts/macros.md). + +Do notice that the query is not sourcing from any other table. This means the job we are creating will not have any +[dependency](../concepts/dependency.md) unless we manually specify so in the job specification YAML file. +However, if for any reason you are querying from another resource and want to ignore the dependency, add +`@ignoreupstream` annotation just before the table name, for example: + +```sql +SELECT column1, column2, column3 +FROM `sample-project.playground.source1` s1 +LEFT JOIN /* @ignoreupstream */ +`sample-project.playground.source2` s2 +ON (s1.id = s2.s1_id) +WHERE +DATE(`load_timestamp`) >= DATE('{{.DSTART}}') +AND DATE(`load_timestamp`) < DATE('{{.DEND}}'); +``` + +### Adding Hook + +There might be a certain operation that you might want to run before or after the Job. Please go through the +[concept](../concepts/job.md) to know more about it. + +For this guide, let’s add a post hook that will audit our BigQuery data using [Predator](https://github.com/raystack/predator). +You can find the Predator plugin YAML file [here](https://github.com/raystack/predator/blob/main/optimus-plugin-predator.yaml) +and have the plugin installed in your [server](../server-guide/installing-plugins.md) and [client](installing-plugin.md). + +In order to add a hook to an existing Job, run the following command and answer the corresponding prompts: + +```shell +$ optimus job addhook +? Please choose the namespace: sample_namespace +? Select a Job sample-project.playground.table1 +? Filter expression for extracting transformation rows? __PARTITION__ >= date("{{ .DSTART | Date }}") AND __PARTITION__ < date("{{ .DEND | Date }}") +? Specify the profile/audit result grouping field (empty to not group the result) +? Choose the profiling mode complete + +Hook successfully added to sample-project.playground.table1 +``` + +With the above prompt, we're adding the predator hook post the execution of the primary job. Filter expression +configuration and the rest of the questions are specific to a predator hook, and it might be different for other hooks. + +After this, existing job.yaml file will get updated with the new hook config. + +```yaml +hooks: + - name: predator + config: + AUDIT_TIME: "{{.EXECUTION_TIME}}" + BQ_DATASET: "{{.TASK__DATASET}}" + BQ_PROJECT: "{{.TASK__PROJECT}}" + BQ_TABLE: "{{.TASK__TABLE}}" + FILTER: __PARTITION__ >= date("{{ .DSTART | Date }}") AND __PARTITION__ < date("{{ .DEND | Date }}") + GROUP: "" + MODE: complete + PREDATOR_URL: "{{.GLOBAL__PREDATOR_HOST}}" + SUB_COMMAND: profile_audit +``` diff --git a/docs/docs/client-guide/installing-plugin.md b/docs/docs/client-guide/installing-plugin.md new file mode 100644 index 0000000000..9999fa2986 --- /dev/null +++ b/docs/docs/client-guide/installing-plugin.md @@ -0,0 +1,10 @@ +# Installing Plugin in Client +Creating job specifications requires a plugin to be installed in the system caller. To simplify the installation, +Optimus CLI can sync the YAML plugins supported and served by the Optimus server (with host as declared in project +config) using the following command: + +```shell +$ optimus plugin sync -c optimus.yaml +``` + +Note: This will install plugins in the `.plugins` folder. diff --git a/docs/docs/client-guide/manage-bigquery-resource.md b/docs/docs/client-guide/manage-bigquery-resource.md new file mode 100644 index 0000000000..2acd9b6a5b --- /dev/null +++ b/docs/docs/client-guide/manage-bigquery-resource.md @@ -0,0 +1,112 @@ +# Manage BigQuery Resource + +Below is the list of the resource types that Optimus supported: + +| Type | Description | +|----------------|--------------------------------------------------------------------------------------------------------------------| +| dataset | Resource name format: [project].[dataset]
Spec can includes: table_expiration, description | +| table | Resource name format: [project].[dataset].[table]
Spec can includes: schema, partition, cluster, description | +| view | Resource name format: [project].[dataset].[view]
Spec can includes: view_query, description | +| external_table | Resource name format: [project].[dataset].[table]
Spec can include: schema, source, description | + +You can create any of the above jobs using the same following format: +```shell +$ optimus resource create +``` + +Make sure to put the correct resource type as you are intended. Once you fill in the command prompt questions, Optimus will create a file (resource.yaml) in the configured datastore directory. Below is an example of each of the type’s resource specifications. + +## Dataset +```yaml +version: 1 +name: sample-project.playground +type: dataset +labels: + usage: documentation + owner: optimus +spec: + description: "example description" + table_expiration: 24 # in hours +``` + +## Table +```yaml +version: 1 +name: sample-project.playground.sample_table +type: table +labels: + usage: documentation + owner: optimus +spec: + description: "example description" + schema: + - name: column1 + type: INTEGER + - name: column2 + type: TIMESTAMP + description: "example field 2" + mode: required # (repeated/required/nullable), default: nullable + - name: column3 + type: STRUCT + schema: # nested struct schema + - name: column_a_1 + type: STRING + cluster: + using: [column1] + partition: # leave empty as {} to partition by ingestion time + field: column2 # column name + type: day # day/hour, default: day +``` + + + +## View +```yaml +version: 1 +name: sample-project.playground.sample_view +type: view +labels: + usage: documentation + owner: optimus +spec: + description: "example description" + view_query: | + Select * from sample-project.playground.sample_table +``` + + +## External Table +```yaml +version: 1 +name: sample-project.playground.sample_table +type: external_table +labels: + usage: documentation + owner: optimus +spec: + description: "example description" + schema: + - name: column1 + type: INTEGER + - name: column2 + type: TIMESTAMP + description: "example field 2" + source: + type: google_sheets + uris: + - https://docs.google.com/spreadsheets/d/spreadsheet_id + config: + range: Sheet1!A1:B4 # Range of data to be ingested in the format of [Sheet Name]![Cell Range] + skip_leading_rows: 1 # Row of records to skip +``` + +## Upload Resource Specifications +Once the resource specifications are ready, you can upload all resource specifications using the below command: +```shell +$ optimus resource upload-all --verbose +``` + + +The above command will try to compare the incoming resources to the existing resources in the server. It will create +a new resource if it does not exist yet, and modify it if exists, but will not delete any resources. Optimus does not +support BigQuery resource deletion nor the resource record in the Optimus server itself yet. diff --git a/docs/docs/client-guide/managing-project-namespace.md b/docs/docs/client-guide/managing-project-namespace.md new file mode 100644 index 0000000000..28eda7e612 --- /dev/null +++ b/docs/docs/client-guide/managing-project-namespace.md @@ -0,0 +1,16 @@ +# Managing Project & Namespace + +Optimus provides a command to register a new project specified in the client configuration or update if it exists: +```shell +$ optimus project register +``` + +You are also allowed to register the namespace by using the with-namespaces flag: +```shell +$ optimus project register --with-namespaces +``` + +You can also check the project configuration that has been registered in your server using: +```shell +$ optimus project describe +``` diff --git a/docs/docs/client-guide/managing-secrets.md b/docs/docs/client-guide/managing-secrets.md new file mode 100644 index 0000000000..cd6db1be35 --- /dev/null +++ b/docs/docs/client-guide/managing-secrets.md @@ -0,0 +1,60 @@ +# Managing Secrets +During job execution, specific credentials are needed to access required resources, for example, BigQuery credential +for BQ to BQ tasks. Users are able to register secrets on their own, manage them, and use them in tasks and hooks. +Please go through [concepts](../concepts/secret.md) to know more about secrets. + +Before we begin, let’s take a look at several mandatory secrets that is used for specific use cases in Optimus. + +| Secret Name | Description | +|--------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| STORAGE | To store compiled jobs if needed. | +| SCHEDULER_AUTH | Scheduler credentials. For now, since Optimus only supports Airflow, this will be Airflow [username:password] | +| BQ_SERVICE_ACCOUNT | Used for any operations involving BigQuery, such as job validation, deployment, run for jobs with BQ to BQ transformation task, as well as for managing BigQuery resources through Optimus. | + + +## Registering secret +Register a secret by running the following command: +```shell +$ optimus secret set someSecret someSecretValue +``` + +By default, Optimus will encode the secret value. However, to register a secret that has been encoded, run the following +command instead: +```shell +$ optimus secret set someSecret encodedSecretValue --base64 +``` + +There is also a flexibility to register using an existing secret file, instead of providing the secret value in the command. +```shell +$ optimus secret set someSecret --file=/path/to/secret +``` + +Secret can also be set to a specific namespace which can only be used by the jobs/resources in the namespace. +To register, run the following command: +```shell +$ optimus secret set someSecret someSecretValue --namespace someNamespace +```` + +Please note that registering a secret that already exists will result in an error. Modifying an existing secret +can be done using the Update command. + +## Updating a secret +The update-only flag is generally used when you explicitly only want to update a secret that already exists and doesn't want to create it by mistake. +```shell +$ optimus secret set someSecret someSecretValue --update-only +``` + +It will return an error if the secret to update does not exist already. + + +## Listing secrets +The list command can be used to show the user-defined secrets which are registered with Optimus. It will list the namespace associated with a secret. +```shell +$ optimus secret list +Secrets for project: optimus-local +NAME | DIGEST | NAMESPACE | DATE +-------------+----------------------------------------------+-----------+---------------------- +secret1 | SIBzsgUuHnExBY4qSzqcrlrb+3zCAHGu/4Fv1O8eMI8= | * | 2022-04-12T04:30:45Z +``` + +It shows a digest for the encrypted secret, so as not to send the cleartext password on the network. diff --git a/docs/docs/client-guide/organizing-specifications.md b/docs/docs/client-guide/organizing-specifications.md new file mode 100644 index 0000000000..d954b1a0b7 --- /dev/null +++ b/docs/docs/client-guide/organizing-specifications.md @@ -0,0 +1,98 @@ +# Organizing Specifications +Optimus supports two ways to deploy specifications +- REST/GRPC +- Optimus CLI deploy command + +When using Optimus CLI to deploy, either manually or from a CI pipeline, it is advised to use a version control system +like git. Here is a simple directory structure that can be used as a template for jobs and datastore resources, +assuming there are 2 namespaces in a project. + +``` +. +├── optimus.yaml +├── README.md +├── namespace-1 +│ ├── jobs +| │ ├── job1 +| │ ├── job2 +| │ └── this.yaml +│ └── resources +| ├── bigquery +│ │ ├── table1 +│ │ ├── table2 +| | └── this.yaml +│ └── postgres +│ └── table1 +└── namespace-2 +└── jobs +└── resources +``` + + +You might have also noticed there are `this.yaml` files being used in some directories. This file is used to share a +single set of configurations across multiple sub-directories. For example, if you create a file at +/namespace-1/jobs/this.yaml, then all subdirectories inside /namespaces-1/jobs will inherit this config as defaults. +If the same config is specified in subdirectory, then subdirectory will override the parent defaults. + +For example a this.yaml in `/namespace-1/jobs` +```yaml +version: 1 +schedule: + interval: @daily +task: + name: bq2bq + config: + BQ_SERVICE_ACCOUNT: "{{.secret.BQ_SERVICE_ACCOUNT}}" +behavior: + depends_on_past: false + retry: + count: 1 + delay: 5s +``` + +and a job.yaml in `/namespace-1/jobs/job1` +```yaml +name: sample_replace +owner: optimus@example.io +schedule: + start_date: "2020-09-25" + interval: 0 10 * * * +behavior: + depends_on_past: true +task: + name: bq2bq + config: + project: project_name + dataset: project_dataset + table: sample_replace + load_method: REPLACE +window: + size: 48h + offset: 24h +``` + +will result in final computed job.yaml during deployment as +```yaml +version: 1 +name: sample_replace +owner: optimus@example.io +schedule: + start_date: "2020-10-06" + interval: 0 10 * * * +behavior: + depends_on_past: true + retry: + count: 1 + delay: 5s +task: + name: bq2bq + config: + project: project_name + dataset: project_dataset + table: sample_replace + load_method: REPLACE + BQ_SERVICE_ACCOUNT: "{{.secret.BQ_SERVICE_ACCOUNT}}" +window: + size: 48h + offset: 24h +``` diff --git a/docs/docs/client-guide/replay-a-job.md b/docs/docs/client-guide/replay-a-job.md new file mode 100644 index 0000000000..11f814ab16 --- /dev/null +++ b/docs/docs/client-guide/replay-a-job.md @@ -0,0 +1,37 @@ +# Replay a Job (Backfill) +Some old dates of a job might need to be re-run (backfill) due to business requirement changes, corrupt data, or other +various reasons. Optimus provides a way to do this using Replay. Please go through [concepts](../concepts/replay-and-backup.md) to know more about it. + +## Run a replay +To run a replay, run the following command: +```shell +$ optimus replay create {job_name} {start_time} {end_time} [flags] +``` + +Example: +```shell +$ optimus replay create sample-job 2023-03-01T00:00:00Z 2023-03-02T15:00:00Z --parallel --project sample-project --namespace-name sample-namespace +``` + +Replay accepts three arguments, first is the DAG name that is used in Optimus specification, second is the scheduled +start time of replay, and third is the scheduled end time (optional) of replay. + +Once your request has been successfully replayed, this means that Replay has cleared the requested runs in the scheduler. +Please wait until the scheduler finishes scheduling and running those tasks. + +## Get a replay status +You can check the replay status using the replay ID given previously and use in this command: +```shell +$ optimus replay status {replay_id} [flag] +``` + +You will see the latest replay status including the status of each run of your replay. + +## Get list of replays +List of recent replay of a project can be checked using this sub command: +```shell +$ optimus replay list [flag] +``` + +Recent replay ID including the job, time window, replay time, and status will be shown. To check the detailed status +of a replay, please use the status sub command. diff --git a/docs/docs/client-guide/setting-up-alert.md b/docs/docs/client-guide/setting-up-alert.md new file mode 100644 index 0000000000..86c57d9cac --- /dev/null +++ b/docs/docs/client-guide/setting-up-alert.md @@ -0,0 +1,35 @@ +# Setting up Alert to Job + +There are chances that your job is failing due to some reason or missed the SLA. For these cases, you might want to set +the alerts and get notified as soon as possible. + +## Supported Events + +| Event Type | Description | +|------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| failure | Triggered when job run status is failed. | +| sla_miss | Triggered when the job run does not complete within the duration that you expected. Duration should be specified in the config and should be in string duration. | + + +## Supported Channels + +| Channel | Description | +|-----------|---------------------------------------------------------------------------------------------| +| Slack | Channel/team handle or specific user | +| Pagerduty | Needing `notify_` secret with pagerduty integration key/routing key | + + +## Sample Configuration + +```yaml +behavior: +notify: +- 'on': failure/sla_miss + config : + duration : 2h45m + channels: + - slack://#slack-channel or @team-group or user&gmail.com + - pagerduty://#pagerduty_service_name +``` + + diff --git a/docs/docs/client-guide/uploading-jobs-to-scheduler.md b/docs/docs/client-guide/uploading-jobs-to-scheduler.md new file mode 100644 index 0000000000..9c031319c5 --- /dev/null +++ b/docs/docs/client-guide/uploading-jobs-to-scheduler.md @@ -0,0 +1,13 @@ +# Uploading Job to Scheduler + +Compile and upload all jobs in the project by using this command: +```shell +$ optimus scheduler upload-all +``` +_Note: add --config flag if you are not in the same directory with your client configuration (optimus.yaml)._ + +This command will compile all of the jobs in the project to Airflow DAG files and will store the result to the path +that has been set as `STORAGE_PATH` in the project configuration. Do note that `STORAGE` secret might be needed if +the storage requires a credential. + +Once you have the DAG files in the storage, you can sync the files to Airflow as you’d like. diff --git a/docs/docs/client-guide/verifying-jobs.md b/docs/docs/client-guide/verifying-jobs.md new file mode 100644 index 0000000000..7b020cf55e --- /dev/null +++ b/docs/docs/client-guide/verifying-jobs.md @@ -0,0 +1,41 @@ +# Verifying the Jobs +Minimize the chances of having the job failed in runtime by validating and inspecting it before deployment. + +## Validate Jobs +Job validation is being done per namespace. Try it out by running this command: + +```shell +$ optimus job validate --namespace sample_namespace --verbose +``` + + +Make sure you are running the above command in the same directory as where your client configuration (optimus.yaml) +is located. Or if not, you can provide the command by adding a config flag. + +By running the above command, Optimus CLI will try to fetch all of the jobs under sample_namespace’s job path that +has been specified in the client configuration. The verbose flag will be helpful to print out the jobs being processed. +Any jobs that have missing mandatory configuration, contain an invalid query, or cause cyclic dependency will be pointed out. + +## Inspect Job +You can try to inspect a single job, for example checking what are the upstream/dependencies, does it has any downstream, +or whether it has any warnings. This inspect command can be done against a job that has been registered or not registered +to your Optimus server. + +To inspect a job in your local: +```shell +$ optimus job inspect +``` + + +To inspect a job in the server: +```shell +$ optimus job inspect --server +``` + +You will find mainly 3 sections for inspection: +- **Basic Info**: + Optimus will print the job’s specification, including the resource destination & sources (if any), and whether it has any soft warning. +- **Upstreams**: + Will prints what are the jobs that this job depends on. Do notice there might be internal upstreams, external (cross-server) upstreams, HTTP upstreams, and unknown upstreams (not registered in Optimus). +- **Downstreams**: + Will prints what are the jobs that depends on this job. diff --git a/docs/docs/client-guide/work-with-extension.md b/docs/docs/client-guide/work-with-extension.md new file mode 100644 index 0000000000..df28832506 --- /dev/null +++ b/docs/docs/client-guide/work-with-extension.md @@ -0,0 +1,178 @@ +# Work with Extension +Extension helps you to include third-party or arbitrary implementation as part of Optimus. Currently, the extension is +designed for when the user is running it as CLI. + +## Warning +Extension is basically an executable file outside Optimus. *We do not guarantee whether an extension is safe or not*. +We suggest checking the extension itself, whether it is safe to run in your local or not, before installing and running it. + +## Limitation +Extension is designed to be similar to [GitHub extension](https://cli.github.com/manual/gh_extension). However, since +it's still in the early stage, some limitations are there. +- extension is only an executable file +- installation only looks at the GitHub asset according to the running system OS and Architecture +- convention for extension: + - extension repository should follow optimus-extension-[name of extension] (example: [optimus-extension-valor](https://github.com/gojek/optimus-extension-valor)) + - asset being considered is binary with suffix ...[OS]-[ARC] (example: when installing valor, if the user's OS is + - Linux and the architecture is AMD64, then installation will consider valor_linux-amd64 as binary to be executed) + +## Creating +Extension is designed to be open. Anyone could create their own extension. And as long as it is available, +anyone could install it. In order to create it, the following are the basic steps to do: + +- Decide the name of the extension, example: *valor* +- Create a GitHub repository that follows the convention, example: *optimus-extension-valor* +- Put some implementation and asset with the name based on the convention, example: *valor_linux-amd64, valor_darwin-amd64*, and more. +- Ensure it is available for anyone to download + +## Commands +Optimus supports some commands to help operate on extension. + +### Installation +You can run the installation using Optimus sub-command install under the extension. In order to install an extension, run the following command: +```shell +$ optimus extension install REMOTE [flags] +``` + +You can use the *--alias* flag to change the command name, since by default, Optimus will try to figure it out by itself. +Although, during this process, sometimes an extension name conflicts with the reserved commands. This flag helps to +resolve that. But, do note that this flag cannot be used to rename an *installed* extension. To do such a thing, check rename. + +REMOTE is the Github remote path where to look for the extension. REMOTE can be in the form of +- OWNER/PROJECT +- github.com/OWNER/PROJECT +- https://www.github.com/OWNER/PROJECT + +One example of such an extension is Valor. So, going back to the example above, installing it is like this: +```shell +$ optimus extension install gojek/optimus-extension-valor@v0.0.4 +``` +or +```shell +$ optimus extension install github.com/gojek/optimus-extension-valor@v0.0.4 +``` +or +```shell +$ optimus extension install https://github.com/gojek/optimus-extension-valor@v0.0.4 +``` + +Installation process is then in progress. If the installation is a success, the user can show it by running: +```shell +$ optimus --help +``` + + +A new command named after the extension will be available. For example, if the extension name is *optimus-extension-valor*, +then by default the command named valor will be available. If the user wishes to change it, they can use *--alias* +during installation, or *rename* it (explained later). + +The following is an example when running Optimus (without any command): + +```shell +... +Available Commands: + ... + extension Operate with extension + ... + valor Execute gojek/optimus-extension-valor [v0.0.4] extension + version Print the client version information +... +``` + + +### Executing +In order to execute an extension, make sure to follow the installation process described above. After installation +is finished, simply run the extension with the following command: + +```shell +$ optimus [extension name or alias] +``` + + +Example of valor: +```shell +$ optimus valor +``` + +Operation +The user can do some operations to an extension. This section explains more about the available commands. +Do note that these commands are available on the installed extensions. For more detail, run the following command: + +```shell +$ optimus extension [extension name or alias] +``` + +Example: +```shell +$ optimus extension valor + +The above command shows all available commands for valor extension. + +Output: + +Sub-command to operate over extension [gojek/optimus-extension-valor@v0.0.4] + +USAGE + optimus extension valor [flags] + +CORE COMMANDS + activate activate is a sub command to allow user to activate an installed tag + describe describe is a sub command to allow user to describe extension + rename rename is a sub command to allow user to rename an extension command + uninstall uninstall is a sub command to allow user to uninstall a specified tag of an extension + upgrade upgrade is a sub command to allow user to upgrade an extension command + +INHERITED FLAGS + --help Show help for command + --no-color Disable colored output + -v, --verbose if true, then more message will be provided if error encountered +``` + +### Activate +Activate a specific tag when running extension. For example, if the user has two versions of valor, +which is v0.0.1 and v0.0.2, then by specifying the correct tag, the user can just switch between tag. + +Example: +```shell +$ optimus extension valor activate v0.0.1 +``` + +### Describe +Describes general information about an extension, such information includes all available releases of an extension +in the local, which release is active, and more. + +Example: +```shell +$ optimus extension valor describe +``` + + +### Rename +Rename a specific extension to another command that is not reserved. By default, Optimus tries to figure out the +appropriate command name from its project name. However, sometimes the extension name is not convenient like it +being too long or the user just wants to change it. + +Example: +```shell +$ optimus extension valor rename vl +``` + +### Uninstall +Uninstalls extension as a whole or only a specific tag. This allows the user to do some cleanup to preserve some +storage or to resolve some issues. By default, Optimus will uninstall the extension as a whole. To target a specific tag, +use the flag --tag. + +Example: +```shell +$ optimus extension valor uninstall +``` + +### Upgrade +Upgrade allows the user to upgrade a certain extension to its latest tag. Although the user can use the install command, +using this command is shorter and easier as the user only needs to specify the installed extension. + +Example: +```shell +$ optimus extension valor upgrade +``` + diff --git a/docs/docs/concepts/architecture.md b/docs/docs/concepts/architecture.md index ff7ab644ee..bcfdd2fc17 100644 --- a/docs/docs/concepts/architecture.md +++ b/docs/docs/concepts/architecture.md @@ -1,74 +1,34 @@ # Architecture -Basic building blocks of Optimus are +![Architecture Diagram](/img/docs/OptimusArchitecture.png "OptimusArchitecture") -- Optimus CLI -- Optimus Service -- Optimus Database -- Optimus Plugins -- Scheduler -- Optimus Extension +## CLI -### Overview - -![Architecture Diagram](/img/docs/OptimusArchitecture_dark_07June2021.png "OptimusArchitecture") - -### Optimus CLI - -`optimus` is a command line tool used to interact with the main optimus service and basic scaffolding job -specifications. It can be used to - -- Generate jobs based on user inputs -- Add hooks to existing jobs -- Dump a compiled specification for the consumption of a scheduler -- Deployment of specifications to `Optimus Service` -- Create resource specifications for datastores +Optimus provides a command line interface to interact with the main optimus service and basic scaffolding job +specifications. It can be used to: - Start optimus server +- Create resource specifications for datastores +- Generate jobs & hooks based on user inputs +- Dump a compiled specification for the consumption of a scheduler +- Validate and inspect job specifications +- Deployment of specifications to Optimus Service -Optimus also has an admin flag that can be turned on using `OPTIMUS_ADMIN_ENABLED=1` env flag. -This hides few commands which are used internally during the lifecycle of tasks/hooks -execution. - -### Optimus Service - -Optimus cli can start a service that controls and orchestrates all that Optimus has to -offer. Optimus cli uses GRPC to communicate with the optimus service for almost all the -operations that takes `host` as the flag. Service also exposes few REST endpoints -that can be used with simple curl request for registering a new project or checking -the status of a job, etc. - -As soon as jobs are ready in a repository, a deployment request is sent to the service -with all the specs(normally in yaml) which are parsed and stored in the database. -Once these specs are stored, each of them are compiled to generate a scheduler parsable -job format which will be eventually consumed by a supported scheduler to execute the -job. These compiled specifications are uploaded to an **object store** which gets synced -to the scheduler. - -### Optimus Database - -Specifications once requested for deployment needs to be stored somewhere as a source -of truth. Optimus uses postgres as a storage engine to store raw specifications, job -assets, run details, project configurations, etc. - -### Optimus Plugins - -Optimus itself doesn't govern how a job is supposed to execute the transformation. It -only provides the building blocks which needs to be implemented by a task. A plugin is -divided in two parts, an adapter and a docker image. Docker image contains the actual -transformation logic that needs to be executed in the task and adapter helps optimus -to understand what all this task can do and help in doing it. +## Server -### Scheduler +Optimus Server handles all the client requests from direct end users or from airflow over http & grpc. The functionality +of the server can be extended with the support of various plugins to various data sources & sinks. Everything around +job/resource management is handled by the server except scheduling of jobs. -Job adapters consumes job specifications which eventually needs to be scheduled and -executed via a execution engine. This execution engine is termed here as Scheduler. -Optimus by default recommends using `Airflow` but is extensible enough to support any -other scheduler that satisfies some basic requirements, one of the most important -of all is, scheduler should be able to execute a Docker container. +## Database +Optimus supports postgres as the main storage backend. It is the source of truth for all user specifications, +configurations, secrets, assets. It is the place where all the precomputed relations between jobs are stored. -### Optimus Extension +## Plugins +Currently, Optimus doesn’t hold any logic and is not responsible for handling any specific transformations. This +capability is extended through plugins and users can customize based on their needs on what plugins to use. Plugins can +be defined through a yaml specification. At the time of execution whatever the image that is configured in the plugin +image will be executed. -Optimus extension is a feature in Optimus where the user could extend the -functionality of Optimus itself using third-party or arbitrary -implementation. Currently, extension is designed only for when the -user running it as CLI. +## Scheduler (Airflow) +Scheduler is responsible for scheduling all the user defined jobs. Currently, optimus supports only Airflow as +the scheduler, support for more schedulers can be added. diff --git a/docs/docs/concepts/dependency.md b/docs/docs/concepts/dependency.md new file mode 100644 index 0000000000..cddc78b483 --- /dev/null +++ b/docs/docs/concepts/dependency.md @@ -0,0 +1,16 @@ +# Dependency + +A job can have a source and a destination to start with. This source could be a resource managed by Optimus or +non-managed like an S3 bucket. If the dependency is managed by Optimus, it is obvious that in an ETL pipeline, it is +required for the dependency to finish successfully first before the dependent job can start. + +There are 2 types of dependency depending on how to configure it: + +| Type | Description | +|-----------|-------------------------------------------------------------------------------------------------------------------------------------| +| Inferred | Automatically detected through assets. The logic on how to detect the dependency is configured in each of the [plugins](plugin.md). | +| Static | Configured through job.yaml | + +Optimus also supports job dependency to cross-optimus servers. These Optimus servers are considered external resource +managers, where Optimus will look for the job sources that have not been resolved internally and create the dependency. +These resource managers should be configured in the server configuration. diff --git a/docs/docs/concepts/intervals-and-windows.md b/docs/docs/concepts/intervals-and-windows.md index 7f106913f1..d58c7722da 100644 --- a/docs/docs/concepts/intervals-and-windows.md +++ b/docs/docs/concepts/intervals-and-windows.md @@ -1,55 +1,74 @@ -# Understanding Intervals and Windows +# Intervals and Windows -When defining a new job, you need to define the interval at which it will be triggered. -This parameter can give you a precise value when the job is scheduled for execution but -only a rough estimate exactly when the job is executing. It is very common in a ETL -pipeline to know when the job is exactly executing as well as for what time window -the current transformation will consume the data. Knowledge of what time window this -transformation is consuming data from the source will tell what previous windows -we have already consumed, and we can skip those. - -For example:
-We have a table with 2 columns - -| name | created_at | -| ------- | ---------- | -| Rick | 2021-03-05 | -| Sanchez | 2021-03-06 | -| Serious | 2021-03-07 | -| Sam | 2021-03-07 | - -If we schedule a job for a cron interval of `0 2 * * *` that is daily at 2 AM, and start -the job from `2021-03-06`, on first day job will consume `Rick` as the input of the table -if we apply a filter over the SQL query of this table as +When defining a new job, you need to define the **interval (cron)** at which it will be triggered. This parameter can give +you a precise value when the job is scheduled for execution but only a rough estimate exactly when the job is executing. +It is very common in a ETL pipeline to know when the job is exactly executing as well as for what time window the current +transformation will consume the data. +For example, assume there is a job that querying from a table using below statement: ```sql -Select * from table where -created_at > DATE('{{.DSTART}}') AND -created_at <= DATE('{{.DEND}}') +SELECT * FROM table WHERE +created_at >= DATE('{{.DSTART}}') AND +created_at < DATE('{{.DEND}}') ``` -Where `DSTART` could be replaced at the time of compilation with `2021-03-05` and -`DEND` with `2021-03-06`. This transformation will consume each row every day. Without -the provided filter, we will have to consume all the records which are created till date -inside the table even though the previous rows might already been processed. +**_DSTART_** and **_DEND_** could be replaced at the time of compilation with based on its window configuration. +Without the provided filter, we will have to consume all the records which are created till date inside the table +even though the previous rows might already been processed. -These `DSTART` and `DEND` values of the input window could vary depending on the ETL -job requirement. -- For a simple transformation job executing daily, it would need to consume full day worth of data of yesterday. -- A job might be consuming data for a week/month for an aggregation job, but the data boundaries should be complete, +These _DSTART_ and _DEND_ values of the input window could vary depending on the ETL job requirement. +- For a simple transformation job executing daily, it would need to consume full day work of yesterday’s data. +- A job might be consuming data for a week/month for an aggregation job, but the data boundaries should be complete, not consuming any partial data of a day. -Optimus allows user to define the amount of data window to consume through window configurations. -The configurations act on the schedule_time of the job and applied in order to compute dstart and dend. +## Window Configuration + +Optimus allows user to define the amount of data window to consume through window configurations. The configurations +act on the schedule_time of the job and applied in order to compute _DSTART_ and _DEND_. -- **Truncate_to**: The data window on most of the scenarios needs to be aligned to a well-defined time window - like month start to month end, or week start to weekend with week start being monday, or a complete day. - Inorder to achieve that the truncate_to option is provided which can be configured with either of these values +- **Truncate_to**: The data window on most of the scenarios needs to be aligned to a well-defined time window + like month start to month end, or week start to weekend with week start being monday, or a complete day. + Inorder to achieve that the truncate_to option is provided which can be configured with either of these values "h", "d", "w", "M" through which for a given schedule_time the end_time will be the end of last hour, day, week, month respectively. -- **Offset**: Offset is time duration configuration which enables user to move the `end_time` post truncation. +- **Offset**: Offset is time duration configuration which enables user to move the `end_time` post truncation. User can define the duration like "24h", "2h45m", "60s", "-45m24h", "0", "", "2M", "45M24h", "45M24h30m" - where "h","m","s","M" means hour, month, seconds, Month respectively. + where "h","m","s","M" means hour, month, seconds, Month respectively. - **Size**: Size enables user to define the amount of data to consume from the `end_time` again defined through the duration same as offset. -The above expectation of windowing is properly handled in job spec version 2, version 1 has some limitations in some of these expectations. -You can verify these configurations through `optimus playground command` +For example, previous-mentioned job has `0 2 * * *` schedule interval and is scheduled to run on +**2023-03-07 at 02.00 UTC** with following details: + +| Configuration | Value | Description | +|---------------|-------|----------------------------------------------------------------------------------------| +| Truncate_to | d | Even though it is scheduled at 02.00 AM, data window will be day-truncated (00.00 AM). | +| Offset | -24h | Shifts the window to be 1 day earlier. | +| Size | 24h | Gap between DSTART and DEND is 24h. | + +Above configuration will produce below window: +- _DSTART_: 2023-04-05T00:00:00Z +- _DEND_: 2023-04-06T00:00:00Z + +This means, the query will be compiled to the following query + +```sql +SELECT * FROM table WHERE +created_at >= DATE('2023-04-05T00:00:00Z') AND +created_at < DATE('2023-04-06T00:00:00Z') +``` + +Assume the table content is as the following: + +| name | created_at | +| ------- |------------| +| Rick | 2023-03-05 | +| Sanchez | 2023-03-06 | +| Serious | 2023-03-07 | +| Sam | 2023-03-07 | + +When the job that scheduled at **2023-03-07** runs, the job will consume `Rick` as the input of the table. + +The above expectation of windowing is properly handled in job spec version 2, version 1 has some limitations in some of +these expectations. You can verify these configurations by trying out in below command: +``` +$ optimus playground +``` diff --git a/docs/docs/concepts/job-run.md b/docs/docs/concepts/job-run.md new file mode 100644 index 0000000000..ff7aa83189 --- /dev/null +++ b/docs/docs/concepts/job-run.md @@ -0,0 +1,7 @@ +# Job Run + +A job is mainly created to run on schedule. Let’s take a look at what is happening once a scheduler triggers your job to run. + +![Job Run Flow](/img/docs/Concept_JobRun.png "JobRunFlow") + +Note: to test this runtime interactions on your own, take a look and follow the guide on developer environment [section](https://github.com/raystack/optimus/tree/main/dev). diff --git a/docs/docs/concepts/job.md b/docs/docs/concepts/job.md new file mode 100644 index 0000000000..72dfae72ab --- /dev/null +++ b/docs/docs/concepts/job.md @@ -0,0 +1,101 @@ +# Job + +A Job is the fundamental unit of the data pipeline which enables a data transformation in the warehouse of choice. +A user can configure various details mentioned below for the job: + +- Schedule interval +- Date from when a transformation should start executing +- Task & Hooks +- Assets needed for transformation +- Alerts + +Job specifications are being compiled to later be processed by the scheduler. Optimus is using Airflow as the scheduler, +thus it is compiling the job specification to DAG (_Directed Acryclic Graph_) file. + +Each of the DAG represents a single job, which consists of: + +- Airflow task(s). Transformation tasks and hooks will be compiled to Airflow tasks. +- Sensors, only if the job has dependency. + +Each job has a single base transformation, we call them **Task** and might have the task pre or/and post operations, +which are called **Hooks**. + +## Task + +A task is a main transformation process that will fetch data, transform as configured, and sink to the destination. +Each task has its own set of configs and can inherit configurations from a global configuration store. + +Some examples of task are: + +- BQ to BQ task +- BQ to Email task +- Python task +- Tableau task +- Etc. + +## Hook + +Hooks are the operations that you might want to run before or after a task. A hook is only associated with a single +parent although they can depend on other hooks within the same job. There can be one or many or zero hooks for a Job as +configured by the user. Some examples of hooks are: + +- [Predator](https://github.com/raystack/predator) (Profiling & Auditing for BQ) +- Publishing transformed data to Kafka +- Http Hooks + +Each hook has its own set of configs and shares the same asset folder as the base job. Hook can inherit configurations +from the base transformation or from a global configuration store. + +The fundamental difference between a hook and a task is, a task can have dependencies over other jobs inside the +repository whereas a hook can only depend on other hooks within the job. + +## Asset + +There could be an asset folder along with the job.yaml file generated via optimus when a new job is created. This is a +shared folder across base transformation task and all associated hooks. Assets can use macros and functions powered by +[Go templating engine](https://golang.org/pkg/text/template/). + +Section of code can be imported from different asset files using template. For example: + +- File partials.gtpl + +```gotemplate +DECLARE t1 TIMESTAMP; +``` + +- Another file query.sql + +```gotemplate +{{template "partials.gtpl"}} +SET t1 = '2021-02-10T10:00:00+00:00'; +``` + +During execution query.sql will be rendered as: + +```gotemplate +DECLARE t1 TIMESTAMP; +SET t1 = '2021-02-10T10:00:00+00:00'; +``` + +whereas **partials.gtpl** will be left as it is because file was saved with .gtpl extension. + +Similarly, a single file can contain multiple blocks of code that can function as macro of code replacement. For example: + +- file.data + +```gotemplate +Name: {{ template "name"}}, Gender: {{ template "gender" }} +``` + +- partials.gtpl + +```gotemplate +{{- define "name" -}} Adam {{- end}} +{{- define "gender" -}} Male {{- end}} +``` + +This will render file.data as + +``` +Name: Adam, Gender: Male +``` diff --git a/docs/docs/concepts/macros.md b/docs/docs/concepts/macros.md new file mode 100644 index 0000000000..fef5052ab6 --- /dev/null +++ b/docs/docs/concepts/macros.md @@ -0,0 +1,14 @@ +# Macros +Macros are special variables that will be replaced by actual values when before execution. Custom macros are not +supported yet. + +Below listed the in built macros supported in optimus. + +| Macros | Description | +| -------------------- |---------------------------------------------------------------------------------| +| {{.DSTART}} | start date/datetime of the window as 2021-02-10T10:00:00+00:00 that is, RFC3339 | +| {{.DEND}} | end date/datetime of the window, as RFC3339 | +| {{.JOB_DESTINATION}} | full qualified table name used in DML statement | +| {{.EXECUTION_TIME}} | timestamp when the specific job run starts | + +Take a detailed look at the windows concept and example [here](intervals-and-windows.md). diff --git a/docs/docs/concepts/namespace.md b/docs/docs/concepts/namespace.md new file mode 100644 index 0000000000..f6613be247 --- /dev/null +++ b/docs/docs/concepts/namespace.md @@ -0,0 +1,5 @@ +# Namespace + +A namespace represents a grouping of specified jobs and resources which are accessible only through the namespace owners. +You may override the project configuration or define the configuration locally at the namespace level. A namespace always +belongs to a Project. All Namespaces of a Project share the same infrastructure and the Scheduler. diff --git a/docs/docs/concepts/overview.md b/docs/docs/concepts/overview.md deleted file mode 100644 index 3e91d32c11..0000000000 --- a/docs/docs/concepts/overview.md +++ /dev/null @@ -1,513 +0,0 @@ -# Overview - -## Optimus Project - -A Project/Tenant represents a group of Jobs, Resources, Scheduler with the specified -configurations and infrastructure. A Project contains multiple user created Namespaces, -and each Namespace contains multiple Jobs/Hooks and configurations. - -## Namespace - -A Namespace represents a grouping of specified Jobs and Resources which can be accessible -only through the namespace owners. User may override the Project configuration or define -configuration locally at the namespace level. A namespace always belongs to a Project. -All Namespaces of a Project share same infrastructure and the Scheduler. They share all -the accesses and secrets provided by the Project, however, they cannot access or modify -the Jobs and Datastore Resources of other namespaces. - -A use case for Namespace could be when multiple teams want to re-use the existing -infrastructure but want to maintain their specifications like Jobs, Resources etc -independently. The namespace's name can be chosen by user or can be provided by the -authentication service. - -## Optimus cli - -Optimus provides a cli used to start Optimus service using `serve` command and a -lot of other features like interacting with the remote/local optimus service, bootstrapping -specifications, validating, testing, etc. Although it is not necessary to use cli -and GRPC/REST can also be used directly which is what CLI does internally for communication -with the service. - -## Job - -A Job is the fundamental unit of the data pipeline which enables a data transformation -in the warehouse of choice. A job has all the basic details required to perform a scheduled -operation few of which are: - -- Schedule interval -- Date from when a transformation should start executing -- How much data this job will consume at every transformation - -Each job has a single base transformation, we call them **Transformer** or **Task**. -Some examples of jobs include: - -1. BQ2BQTask - transformation from BigQuery to BigQuery in SQL -2. SparkSQLTask - transformation from BQ/GCS to BQ/GCS in SparkSQL -3. PySparkTask - transformation using python. - -## Hook - -Hooks are the operations that you might want to run before or after a Job. A hook is -only associated with a single parent although they can depend on other hooks within -the same job. There can be many or zero hooks for a Job as configured by the user. -Some examples of hooks are: - -1. Transporter(BQ/GCS to Kafka) -2. Predator(Auditing & Profiling for BQ) -3. BeastLagChecker -4. Http Hooks -5. Tableau view updates - -Each hook has its own set of configs and share the same asset folder as the base job. -Hook can inherit configurations from the base transformation or from a global configuration -store. - -> The fundamental difference between a hook and a task is, task can have dependencies -> over other jobs inside the repository whereas hook can only depend on other hooks within -> the job. - -## Job Specification - -Optimus has a specification repository that holds all the details required to -define a scheduled operation. Repository has a fixed folder structure which is -maintained by Optimus CLI. Users can create and delete the jobs from the -repository using either optimus CLI or a simple text editor like -[VSCode](https://code.visualstudio.com/download). A sample command to create -a new job is "optimus create job", calling this will ask for a -few inputs which are required for the execution of this job and -leave rest for the user to modify of its own eg, the SQL. - -If you're new to YAML and want to learn more, see [Learn YAML in Y minutes.](https://learnxinyminutes.com/docs/yaml/) - -Following is a sample job specification: - -```yaml -# specification version, for now just keep it fixed unless optimus has any -# breaking change -version: 1 - -# unique name for the job, try to use simple ascii characters and less than 200 chars -# to keep scheduler db's happy -name: example_job - -# owner of the job -owner: example@example.com - -# description of this job, what this do -description: sample example job - -# configure when it should start, when the job should stop executing and what -# interval scheduler should use for execution -schedule: - # time format should be RFC3339 - start_date: "2021-02-18" - end_date: "2021-02-25" - - # supports standard cron notations - # leave as empty string for manually triggered jobs - interval: 0 3 * * * - -# extra modifiers to change the behavior of the job -behavior: - # should the job wait for previous runs to finish successfully before executing - # next run, this will make it execute in sequence - depends_on_past: false - - # if start_date is set in the past, and catchup is true, it will allow scheduler - # to automatically backfill history executions till it reaches today - catch_up: true - - # retry behaviour of this job if it fails to successfully complete in first try - retry: - # maximum number of tries before giving up, cannot be 0 - count: 3 - - # delay between retries - delay: "15m" - - # allow progressive longer waits between retries by using exponential backoff algorithm - # on retry delay (delay will be converted into seconds) - exponential_backoff: false - - # send a notification to routing channel based on an event - notify: - - # event to listen for - # possible options failure/sla_miss - on: failure - - # list of routes that will recieve the notification - channel: - # email id of a user in slack - - slack://example@example.com - # slack channel - - slack://#optimus-devs - # slack user group - - slack://@optimus-devs - - # additional configs required for certain events like sla_miss - config: - - duration: 2h - -# transformation task configuration for this job -task: - # name of the task type - name: bq2bq - - # configuration passed to the task before execution - config: - PROJECT: example - DATASET: data - TABLE: hello_table - LOAD_METHOD: APPEND - SQL_TYPE: STANDARD - PARTITION_FILTER: 'event_timestamp >= "{{.DSTART}}" AND event_timestamp < "{{.DEND}}"' - - # time window, could be used by task for running incremental runs instead of processing - # complete past data at every iteration - window: - # size of incremental window - # eg: 1h, 6h, 48h, 2h30m - size: 24h - - # shifting window forward of backward in time, by default it is yesterday - offset: "0" - - # truncate time window to nearest hour/day/week/month - # possible values: h/d/w/M - truncate_to: d - -# labels gets passed to task/hooks -# these can be used to attach metadata to running transformation -# discovering usage, identifying cost, grouping identities, etc -labels: - orchestrator: optimus - -# static dependencies that can be used to wait for upstream job to finish -# before this job can be started for execution -dependencies: - # list `job: ` - - job: sample_internal_job - # provide where the upstream dependency is stored, intra/inter/extra - # intra: within this project - # inter: within optimus but cross project - # extra: outside optimus[not properly supported] - # optional, default: intra - type: intra - - - # inter dependencies should prefix job name with project name divided by - # a forward slash / - job: cross_project/sample_external_job - type: inter - -# adhoc operations marked for execution at different hook points -# accepts a list -hooks: - - # name of the hook - name: transporter - - # where & when to attach this process - type: post - - # configuration passed to hook before execution - config: - KAFKA_TOPIC: optimus_example-data-hello_table - - # configuration being inherited from project level variables - PRODUCER_CONFIG_BOOTSTRAP_SERVERS: "{{.GLOBAL__TransporterKafkaBroker}}" - - PROTO_SCHEMA: example.data.HelloTable - -# contains additional metadata information -metadata: - # contains metadata on the resource of a job - resource: - # defines the requested configuration of a resource for a job - request: - # defines the requested memory for a job's resource - memory: 128Mi - # defines the requested cpu for a job's resource - cpu: 250m - # defines the limit configuration of a resource for a job - limit: - # defines the limit memory for a job's resource - memory: 256Mi - # defines the limit cpu for a job's resource - cpu: 500m - # contains airflow specific configuration - airflow: - # defines the pool to be assigned for this job - pool: poolA - # defines the queue to be assigend for this job - queue: queueA -``` - -`resource` under metadata follows most of the convention specified [here](https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container). -Though it's similar, the difference is only on how to write it in the specification YAML file. - -`airflow` pool and queue are to be sent to the BaseOperator. See the specification [here](https://airflow.apache.org/docs/apache-airflow/stable/concepts/pools.html). - -Configuring `metadata`, including but not limited to `resource` and `airflow`, is **NOT** mandatory. - -## Macros & Templates - -Optimus allows using pre-defined macros/templates to make the pipelines more -dynamic and extensible. Macros can be used in Job/Hooks configurations or Assets. -Some macros are: - -- `"{{.DEND}}"`: this macro is replaced with the current execution date - (in yyyy-mm-ddThh:mm:ssZ format) of the task (note that this is the execution date - of when the task was supposed to run, not when it actually runs). It would - translate to a timestamp in runtime. eg, "2021-01-30T00:00:00Z" -- `"{{.DSTART}}"`: the value of this macro is DEND minus the task window. For - the DAILY task window, DSTART is one day behind DEND, if the task window is - weekly, DSTART is 7 days before DEND. -- `"{{.EXECUTION_TIME}}"`: the value of this marco is always the current timestamp. - -You can use these in either `job.yml` configs or in assets. For example: - -- Asset for a SQL query `query.sql` - -```sql -select CONCAT("Hello, ", "{{.DEND}}") as message -``` - -- Configuration in `job.yml` - -```yaml -version: 1 -name: example_job -... omitting few configs ... -hooks: -- name: transporter - config: - BQ_TABLE: hello_table - FILTER_EXPRESSION: event_timestamp >= '{{.DSTART}}' AND event_timestamp < '{{.DEND}}' -``` - -Macros can be chained together via pipe-sign with predefined functions. - -- `Date`: Converters Timestamp to Date. For example - -```sql -SELECT * FROM table1 -WHERE DATE(event_timestamp) < '{{ .DSTART|Date }}' -``` - -## Configuration - -Each job specification has a set of configs made with a key value pair. Keys are always -specific to the execution unit and value could be of 3 types. - -- User provided: These inputs are values provided by users at the time of creating - via optimus cli or modifying the job using a text editor. -- Task inherited: Hooks can inherit the configuration values from base transformation and - avoid providing the same thing using `{{.TASK__}}` macro. - For example: - -```yaml -task: - name: bq2bq - config: - DATASET: playground -hooks: - name: myhook - config: - MY_DATASET: { { .TASK__DATASET } } -``` - -- Repository global: Configs that will be shared across multiple jobs and should remain static - can be configured in a global config store as part of tenant registration. These configs are - available to only the registered repository and will remain same for all the jobs. Jobs can access - them via `{{.GLOBAL__}}`. - For example: - -```yaml -task: - name: bq2bq - config: - DATASET: { { .GLOBAL__COMMON_DATASET } } -hooks: - name: myhook - config: - KAFKA_BROKER: { { .GLOBAL__KAFKA_BROKERS } } -``` - -At the moment we only support these configs to be registered via REST API exposed in optimus -which will be discussed in a different section but in near future should be configurable via -a configuration file inside the repository. - -## Assets - -There could be an asset folder along with the `job.yaml` file generated via `optimus` when -a new job is created. This is a shared folder across base transformation task -and all associated hooks. For example, if BQ2BQ task is selected, it should generate a -template `query.sql` file for writing the BigQuery transformation SQL. Assets can use -macros and functions powered by [Go templating engine](https://golang.org/pkg/text/template/). -Optimus also injects few helper functions provided in [sprig](http://masterminds.github.io/sprig/) -library. -For example: - -```sql -{{ $name := "admin" }} -select CONCAT("Hello, ", "{{.name}}") as message -``` - -Section of code can be imported from different asset files using -[template](https://golang.org/pkg/text/template/#hdr-Actions). For example: - -- File `partials.gtpl` - -```sql -DECLARE t1 TIMESTAMP; -``` - -- Another file `query.sql` - -```sql -{{template "partials.gtpl"}} -SET t1 = '2021-02-10T10:00:00+00:00'; -``` - -During execution `query.sql` will be rendered as: - -```sql -DECLARE t1 TIMESTAMP; -SET t1 = '2021-02-10T10:00:00+00:00'; -``` - -whereas `partials.gtpl` will be left as it is because file was saved with `.gtpl` -extension. - -Similarly, a single file can contain multiple blocks of code that can function -as macro of code replacement. For example: - -- `file.data` - -``` - Name: {{ template "name"}}, Gender: {{ template "gender" }} -``` - -- `partials.gtpl` - -``` - {{- define "name" -}} Adam {{- end}} - {{- define "gender" -}} Male {{- end}} -``` - -This will render `file.data` as - -``` -Name: Adam, Gender: Male -``` - -## Scheduler - -A scheduler is one of the core unit responsible for scheduling the jobs for execution -on a defined interval. By default, Optimus uses [Airflow](https://airflow.apache.org/) -as the schedule but does support extending to different schedulers that follow -few guidelines. -TODO: Docs for supporting custom scheduler - -## Dependency Resolver - -A job can have a source and a destination to start with. This source could be a resource -managed by optimus or non-managed like a S3 bucket. If the dependency is managed -by optimus, it is obvious that in an ETL pipeline, it is required for the dependency to -finish successfully first before the dependent job can start. This direct or indirect -dependency can be automatically inferred in job specifications based on task inputs. -For example, in BQ2BQ task, it parses the SQL transformation and look for tables that -are used as source using FROM, JOIN, etc keywords and mark them as the dependency for the -current job. Optimus call this automatic dependency resolution which happens automatically. -There are options to manually specify a dependency using the job name within the same -project if needed to. - -Optimus also supports job dependency to cross optimus servers. These optimus servers are -considered as external resource managers, where optimus will look for the job sources that -have not been resolved internally and create the dependency. These resource managers should -be configured in the server configuration. - -## Priority Resolver - -Schedulers who support "Priorities" to handle the problem of "What to execute first" -can take the advantage of Optimus Priority Resolver. To understand this lets take an -example taking Airflow as the scheduler: - -Let's say we provide limited slots to airflow i.e. 10, that means only 10 tasks can be -executed at a time. Now these tasks could be a [Sensor](https://airflow.apache.org/docs/apache-airflow/stable/_api/airflow/sensors/index.html) -to check if the upstream [DAG](https://airflow.apache.org/docs/apache-airflow/stable/dag-run.html) -is finished, or it could be a task that actually executes the transformation. -The issue is when Airflow don’t know what to prioritise when if all of them are scheduled -to execute at the same time. If airflow keep on scheduling these sensors(which will -never pass because upstream DAG never really got the chance to execute as airflow didn’t schedule -the transformation task) it will get stuck in kind of a deadlock. All those 10 slots -will be filled by sensors again and again and Airflow will take enormous time to schedule -a actual transformation. Its not really a deadlock but very similar and waste a lot of time -finding the correct task to execute like a needle in haystack. So recently we have -taken a story to prioritise these tasks based on how many downstream dependencies are waiting -for it. That is, in a dependency tree, whatever is at the root(depends on nothing) will always -be prioritised first like Standardised layer dags and then it will move to downstream -sensors and tasks. - -This will help fully utilize the Scheduler capabilities. - -## Optimus Plugins - -Optimus's responsibilities are currently divided in two parts, scheduling a transformation [task](#Job) and running one time action to create or modify a [datastore](#Datastore) resource. Defining how a datastore is managed can be easy and doesn't leave many options for configuration or ambiguity although the way datastores are implemented gives developers flexibility to contribute additional type of datastore, but it is not something we do every day. - -Whereas tasks used in jobs that define how the transformation will execute, what configuration does it need as input from user, how does this task resolves dependencies between each other, what kind of assets it might need. These questions are very open and answers to them could be different in different organisation and users. To allow flexibility of answering these questions by developers themselves, we have chosen to make it easy to contribute a new kind of task or even a hook. This modularity in Optimus is achieved using plugins. - -> Plugins are self-contained binaries which implements predefined protobuf interfaces to extend Optimus functionalities. - -Optimus can be divided in two logical parts when we are thinking of a pluggable model, one is the **core** where everything happens which is common for all job/datastore, and the other part which could be variable and needs user specific definitions of how things should work which is a **plugin**. - -## Datastore - -Optimus datastores are managed warehouses that provides CRUD on resources attached to it. Each warehouse supports fixed set of resource types, each type has its own specification schema. - -At the moment, Optimus supports BigQuery datastore for 3 types of resources: - -- [Dataset](https://cloud.google.com/bigquery/docs/datasets-intro) -- [Table](https://cloud.google.com/bigquery/docs/tables-intro) -- [Standard View](https://cloud.google.com/bigquery/docs/views-intro) - -## Secret Management - -A lot of transformation operations require credentials to execute. These credentials (secrets) are needed in some tasks, hooks, and may also be needed in plugin adapters to compute dependencies/compile assets/etc. before the actual transformation even begins. Optimus provides a convenient way to store secrets and make them accessible in containers during the execution. - -Optimus has two sets of secrets, User-Defined secrets & System secrets which are needed for server operations. - -### User-Defined Secrets -Users can easily create, update, and delete their own secrets using CLI or REST API. Secrets can be created in a project level which is accessible from all the namespaces in the project, or can just be created in namespace level. These secrets will then can be used as part of the job spec configuration using macros with their names. Only the secrets created at project & namespace the job belongs to can be referenced. - -### System Secrets -System secrets are available for the entire project and are intended to be managed by only the administrator. These system secrets are being used in server operations, for example storage and scheduler access. Each of the system secrets are prefixed by `_OPTIMUS_`, for example: -``` -_OPTIMUS_someSecret superSecretValue -``` - - -## Replay & Backups - -A job might need to be re-run (backfill) due to business requirement changes or other various reasons. Optimus provides -an easy way to do this using Replay. Replay accepts which job and range of date to be updated, validating it, -and re-run the job tasks. Replay will also figure the dependents of the job and re-run all the downstream jobs within -the same project just after the upstream tasks are finished. - -When validating, Optimus checks if replay with the same job and date are currently running and also checks if the task -scheduler instances are still running to avoid any duplication and conflicts. - -After passing the validation, Replay will clear task instances of the requested job in the scheduler, letting the tasks to be picked -up and run. Replay will frequently check the status of each task from the scheduler (every 5 minutes) to track if -each task is still in progress, failed, or succeeded. - -Optimus also provides Backup to duplicate a resource that can be perfectly used before running Replay. Optimus accepts -which datastore and resource that needs to be backed up and users have a choice to also back up the downstream resources -within the same project. Where the backup result will be located, and the expiry detail can be configured in the project -configuration. - -Both Replay and Backup are provided with Dry Run to simulate all the impacted tasks or resources without actually re-running -the tasks or backing up the resources. - -## Monitoring & Alerting - -TODO diff --git a/docs/docs/concepts/plugin.md b/docs/docs/concepts/plugin.md new file mode 100644 index 0000000000..a926bee89c --- /dev/null +++ b/docs/docs/concepts/plugin.md @@ -0,0 +1,7 @@ +# Plugin +Optimus can provide support for various data warehouses & any third party system to handle all the data transformations +or movement through plugins. You can bring your own plugin by encapsulating all the logic in a docker container. + +Currently, plugins can be defined as YAML or binary executables. YAML plugin provides the questionnaire and default +values for job task’s / hook’s creation, as well as defines the image to execute. While a binary plugin, it is +complementing the YAML plugin by providing support for automated dependency resolution. diff --git a/docs/docs/concepts/project.md b/docs/docs/concepts/project.md new file mode 100644 index 0000000000..aa0004449b --- /dev/null +++ b/docs/docs/concepts/project.md @@ -0,0 +1,4 @@ +# Project + +A project/tenant represents a group of jobs, resources, and a scheduler with the specified configurations and infrastructure. +A project contains multiple user-created namespaces, and each has various jobs and resources. diff --git a/docs/docs/concepts/replay-and-backup.md b/docs/docs/concepts/replay-and-backup.md new file mode 100644 index 0000000000..b4335dd55f --- /dev/null +++ b/docs/docs/concepts/replay-and-backup.md @@ -0,0 +1,21 @@ +# Replay & Backup +A job might need to be re-run (backfill) due to business requirement changes or other various reasons. Optimus provides +an easy way to do this using Replay. Replay accepts which job and range of date to be updated, validates it, and re-runs +the job tasks. + +When validating, Optimus checks if there is any Replay with the same job and date currently running and also checks if +the task scheduler instances are still running to avoid any duplication and conflicts. + +After passing the validation checks, a Replay request will be created and will be processed by the workers based on the +mode chosen (sequential/parallel). To re-run the tasks, Optimus clears the existing runs from the scheduler. + +**Sequential (Default)** + +![Sequential Mode Flow](/img/docs/ReplaySequential.png "SequentialMode") + +**Parallel** + +![Parallel Mode Flow](/img/docs/ReplayParallel.png "ParallelMode") + +Optimus also provides a Backup feature to duplicate a resource that can be perfectly used before running Replay. Where +the backup result will be located, and the expiry detail can be configured in the project configuration. diff --git a/docs/docs/concepts/resource.md b/docs/docs/concepts/resource.md new file mode 100644 index 0000000000..c40d42e60c --- /dev/null +++ b/docs/docs/concepts/resource.md @@ -0,0 +1,14 @@ +# Resource + +A resource is the representation of the warehouse unit that can be a source or a destination of a transformation job. +The warehouse resources can be created, modified, and be read from Optimus, as well as can be backed up as requested. +Each warehouse supports a fixed set of resource types and each type has its own specification schema. +Optimus’ managed warehouse is called Optimus datastore. + +At the moment, Optimus supports BigQuery datastore for these type of resources: +- Dataset +- Table +- Standard View +- External Table + +_Note: BigQuery resource deletion is currently not supported._ diff --git a/docs/docs/concepts/secret.md b/docs/docs/concepts/secret.md new file mode 100644 index 0000000000..e57e9f6b4a --- /dev/null +++ b/docs/docs/concepts/secret.md @@ -0,0 +1,9 @@ +# Secret +A lot of transformation operations require credentials to execute. These credentials (secrets) are needed in some tasks, +hooks, and may also be needed in deployment processes such as dependency resolution. Optimus provides a convenient way +to store secrets and make them accessible in containers during the execution. + +You can easily create, update, and delete your own secrets using CLI or REST API. Secrets can be created at a project +level which is accessible from all the namespaces in the project, or can just be created at the namespace level. These +secrets will then can be used as part of the job spec configuration using macros with their names. Only the secrets +created at the project & namespace the job belongs to can be referenced. diff --git a/docs/docs/contribute/contributing.md b/docs/docs/contribute/contributing.md deleted file mode 100644 index ae2490ed2a..0000000000 --- a/docs/docs/contribute/contributing.md +++ /dev/null @@ -1,37 +0,0 @@ -# Contributing - -First off, thanks for taking the time to contribute! 🌟🥳 - -When contributing to this repository, please first discuss the change you wish to make via issue, -email, or any other method with the owners of this repository before making a change. - -Please note we have a code of conduct, please follow it in all your interactions with the project. - -## Best practices - -- Write clear and meaningful git commit messages. -- If the PR will *completely* fix a specific issue, include `fixes #123` in the PR body (where 123 is the specific issue number the PR will fix. This will automatically close the issue when the PR is merged. -- Make sure you don't include `@mentions` or `fixes` keywords in your git commit messages. These should be included in the PR body instead. -- When you make a PR for small change (such as fixing a typo, style change, or grammar fix), please squash your commits so that we can maintain a cleaner git history. -- Make sure you include a clear and detailed PR description explaining the reasons for the changes, and ensuring there is sufficient information for the reviewer to understand your PR. -- Additional Readings: - - [chris.beams.io/posts/git-commit/](https://chris.beams.io/posts/git-commit/) - - [github.com/blog/1506-closing-issues-via-pull-requests ](https://github.com/blog/1506-closing-issues-via-pull-requests) - - [davidwalsh.name/squash-commits-git ](https://davidwalsh.name/squash-commits-git) - - [https://mtlynch.io/code-review-love/](https://mtlynch.io/code-review-love/) - -## Code of Conduct - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the project - -Things to keep in mind before creating a new commit: - -* Go through the project code conventions. -* Commit [guidelines](https://www.conventionalcommits.org/en/v1.0.0/) -* [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md) diff --git a/docs/docs/contribute/contribution-process.md b/docs/docs/contribute/contribution-process.md new file mode 100644 index 0000000000..ce54863c8b --- /dev/null +++ b/docs/docs/contribute/contribution-process.md @@ -0,0 +1,34 @@ +# Contributing + +First off, thanks for taking the time to contribute! 🌟🥳 + +Before start contributing, feel free to ask questions or initiate conversation via GitHub discussion. +You are also welcome to create issue if you encounter a bug or to suggest feature enhancements. + +Please note we have a code of conduct, please follow it in all your interactions with the project. + +## Best practices + +- Follow the [conventional commit](https://www.conventionalcommits.org/en/v1.0.0/) format for all commit messages. +- Link the PR with the issue. This is mandatory to ensure there is sufficient information for the reviewer to understand + your PR. +- When you make a PR for small change (such as fixing a typo, style change, or grammar fix), please squash your commits + so that we can maintain a cleaner git history. +- Docs live in the code repo under [docs](https://github.com/raystack/optimus/tree/main/docs). Please maintain the docs + and any docs changes can be done in the same PR. +- Avoid force-pushing as it makes reviewing difficult. + +## Code of Conduct + +Examples of behavior that contributes to creating a positive environment include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the project + +Things to keep in mind before creating a new commit: + +- Go through the project code conventions. +- Commit [guidelines](https://www.conventionalcommits.org/en/v1.0.0/) +- [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md) diff --git a/docs/docs/contribute/developer-env-setup.md b/docs/docs/contribute/developer-env-setup.md new file mode 100644 index 0000000000..7544c2f333 --- /dev/null +++ b/docs/docs/contribute/developer-env-setup.md @@ -0,0 +1,4 @@ +# Developer Environment Setup + +To test Optimus including the integration with Airflow, as well as testing the runtime interactions, Optimus provides +a way to simplify setting up the environment. Take a look and follow the guide in dev setup [section](https://github.com/raystack/optimus/tree/main/dev). diff --git a/docs/docs/development/building-plugin.md b/docs/docs/development/building-plugin.md deleted file mode 100644 index 71c92ba5f0..0000000000 --- a/docs/docs/development/building-plugin.md +++ /dev/null @@ -1,953 +0,0 @@ ---- -id: building-plugin -title: Developing plugins ---- - -Optimus's responsibilities are currently divided in two parts, scheduling a transformation [task](../concepts/overview.md#Job) and running one time action to create or modify a [datastore](../concepts/overview.md#Datastore) resource. Defining how a datastore is managed can be easy and doesn't leave many options for configuration or ambiguity although the way datastores are implemented gives developers flexibility to contribute additional type of datastore, but it is not something we do every day. - -Whereas tasks used in jobs that define how the transformation will execute, what configuration does it need as input from user, how does this task resolves dependencies between each other, what kind of assets it might need. These questions are very open and answers to them could be different in different organisation and users. To allow flexibility of answering these questions by developers themselves, we have chosen to make it easy to contribute a new kind of task or even a hook. This modularity in Optimus is achieved using plugins. - -Optimus can be divided in two logical parts when we are thinking of a pluggable model, one is the **core** where everything happens which is common for all job/datastore, and the other part which could be variable and needs user specific definitions of how things should work which is a **plugin**. - -Currently Optimus plugin can be implemented as binary executable. -And `support for yaml` implementation of plugins is also introduced with limited -scope (discussed below). - -Optimus Plugin is just an adapter between optimus and what actually needs to be executed. Actual transformation will be packed in a docker image and Optimus will execute these arbitrary docker images as long as it has access to reach container registry. Plugin provides the optimus server with the info about the docker image. - -> Plugin itself is not executed for transformation but only used for adapting conditions which Optimus requires to be defined for each task. - -## Types of Plugins in Optimus -At the moment mainly there are two types of plugins which optimus supports. These are : ***Hook*** and ***Task*** -Before getting into the difference between two plugins ,we need to get familiar with [Jobs](../concepts/overview.md#Job). - -| Type | Task | Hook | -| ---------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -| Definition | It is the single base transformation in a Job. | It is the operation that we preferably run before or after a Job. | -| Fundamental Difference | It can have dependencies over other jobs inside the Optimus project. | It can have dependencies over other hooks within the job. | -| Configuration | It has its own set of configs and asset directory. | It has its own set of configs and share the same asset directory across all hooks as the base job. | - - -## Supported Use-Cases of Plugins in Optimus - -* Plugin Info Usecases: - * Discover all plugins and list their info. - * Refer - `optimus version` (lists all the plugins available), -* Project Side Usecases : - * Survey to populate job specifications and assets. - * Plugins provide the questionare and default values (default assets for job) to the survey implemtnation in optimus. - * Refer - `optimus job create` -* Server Side Usecases : - * CompileAssets & DependencyResolver - * Theses are currently supported server side behaviour that is delegated to plugins implementations. - * Refer - [transformers](https://github.com/odpf/transformers/blob/main/task/bq2bq/main.go#L274) - -## Binary Implementation of Plugin - -Binary implementation of Plugins are self-contained binaries which implements predefined protobuf interfaces to extend Optimus functionalities. -Binary Plugins are implemented using [go-plugin](https://github.com/hashicorp/go-plugin) developed by Hashicorp used in terraform and other similar products. - -> Plugins can be implemented in any language as long as they can be exported as a single self-contained executable binary and implements a GRPC server. - -It is recommended to use Go currently for writing plugins because of its cross platform build functionality and to reuse protobuf sdk provided within Optimus core. - -> Binary Plugins can potentially modify the behavior of Optimus in undesired ways. Exercise caution when adding new plugins developed by unrecognized developers. - - -## Yaml Implementation of Plugin -Most plugins are expected to implement just the info and project side use-cases (mentioned above) and thease are data-driven i.e., plugin just provide data to optimus. -To simplify the development process of plugins, support for yaml mode of defining plugins is added. - -```go -// representation of a yaml plugin schema in golang - -// below struct definition in golang can be marshalled -// to generate yaml plugins - -type YamlPlugin struct { - // info use-case - Name string `yaml:"name"` - Description string `yaml:"description"` - Plugintype string `yaml:"plugintype"` - Pluginversion string `yaml:"pluginversion"` - Image string `yaml:"image"` - Secretpath string `yaml:"secretpath"` - - // survey use-case - Questions []struct { - Name string `yaml:"name"` - Prompt string `yaml:"prompt"` - Help string `yaml:"help"` - Regexp string `yaml:"regexp"` - Validationerror string `yaml:"validationerror"` - Minlength int `yaml:"minlength"` - Required bool `yaml:"required,omitempty"` - Maxlength int `yaml:"maxlength,omitempty"` - Subquestions []struct { - Ifvalue string `yaml:"ifvalue"` - Questions []struct { - Name string `yaml:"name"` - Prompt string `yaml:"prompt"` - Help string `yaml:"help"` - Multiselect []string `yaml:"multiselect"` - Regexp string `yaml:"regexp"` - Validationerror string `yaml:"validationerror"` - Minlength int `yaml:"minlength"` - Required bool `yaml:"required,omitempty"` - Maxlength int `yaml:"maxlength,omitempty"` - } `yaml:"questions"` - } `yaml:"subquestions,omitempty"` - } `yaml:"questions"` - - // default-static-values use-case - Defaultassets []struct { - Name string `yaml:"name"` - Value string `yaml:"value"` - } `yaml:"defaultassets"` - Defaultconfig []struct { - Name string `yaml:"name"` - Value string `yaml:"value"` - } `yaml:"defaultconfig"` -} -``` -refer to sample implemenation [here](#2a-creating-a-yaml-plugin) -### Limitations of Yaml plugins: - -Here the scope of yaml plugins is limited to drive survey, provide default values for job config and assets and provide plugin info. As majoiry of the plugins are expected to implement subset these use-cases, the support for yaml definitions for plugins is added which simplifies development, packaging and distribution of plugins. - -For plugins that require to enrich optimus server side behaviour, yaml definitions falls short as this would require some code. - -### Validating Yaml plugins: -Also support for validating yaml plugin is added into optimus. -After creating yaml definitions of plugin, one can validate them as below: -```bash -optimus plugin validate --path {{directory of yaml plugins}} -``` - -** Note: The yaml plugin is expected to have file name as `optimus-plugin-{{name}}.yaml` - -** Note: If Both yaml and binary plugin with same name are installed, Yaml implementation is prioritised over the corresponding counterparts in binary implemenation. -## Creating Plugin : Tutorial - -To demonstrate the above mentioned wrapping functionality, let's create a plugin in Go as well as a yaml definition and use python for actual transformation logic. You can choose to fork this [example](https://github.com/kushsharma/optimus-plugins) template and modify it as per your needs or start fresh. To demonstrate how to start from scratch, we will be starting from an empty git repository and build a plugin which will find potential hazardous **Near Earth Orbit** objects every day, let's call it **neo** for short. - -Brief description of Neo is as follows - -- Using [NASA API](https://api.nasa.gov/) we can get count of hazardous objects, their diameter and velocity. -- Task will need two config as input, `RANGE_START`, `RANGE_END` as date time string which will filter the count for this specific period only. -- Execute every day, say at 2 AM. -- Need a secret token that will be passed to NASA api endpoint for each request. -- Output of this object count can be printed in logs for now but in a real use case can be pushed to Kafka topic or written to a database. -- Plugin will be written in **Go** and **Neo** in **python**. - -### 1. Preparing task executor - -Start by initialising an empty git repository with the following folder structure - -```shell -.git -/task - /neo - /executor - /main.py - /requirements.txt - /Dockerfile -README.md -``` - -That is three folders one inside another. This might look confusing for now, a lot of things will, but just keep going. Create an empty python file in executor `main.py`, this is where the main logic for interacting with nasa api and generating output will be. For simplicity, lets use as minimal things as possible. - -Add the following code to `main.py` - -```python -import os -import requests -import json - -def start(): - """ - Sends a http call to nasa api, parses response and prints potential hazardous - objects in near earth orbit - :return: - """ - opt_config = fetch_config_from_optimus() - - # user configuration for date range - range_start = opt_config["envs"]["RANGE_START"] - range_end = opt_config["envs"]["RANGE_END"] - - secret_key = os.environ["SECRET_KEY"] - - # secret token required for NASA API being passed using job spec - api_key = json.loads(secret_key) - if api_key is None: - raise Exception("invalid api token") - - # send the request for given date range - r = requests.get(url="https://api.nasa.gov/neo/rest/v1/feed", - params={'start_date': range_start, 'end_date': range_end, 'api_key': api_key}) - - # extracting data in json format - print("for date range {} - {}".format(range_start, range_end)) - print_details(r.json()) - - return - - -def fetch_config_from_optimus(): - """ - Fetch configuration inputs required to run this task for a single schedule day - Configurations are fetched using optimus rest api - :return: - """ - # try printing os env to see what all we have for debugging - # print(os.environ) - - # prepare request - optimus_host = os.environ["OPTIMUS_HOSTNAME"] - scheduled_at = os.environ["SCHEDULED_AT"] - project_name = os.environ["PROJECT"] - job_name = os.environ["JOB_NAME"] - - r = requests.post(url="http://{}/api/v1/project/{}/job/{}/instance".format(optimus_host, project_name, job_name), - json={'scheduled_at': scheduled_at, - 'instance_name': "neo", - 'instance_type': "TASK"}) - instance = r.json() - - # print(instance) - return instance["context"] - - - -if __name__ == "__main__": - start() -``` - - - -`api_key` is a token provided by nasa during registration. This token will be passed as a parameter in each http call. `SECRET_PATH` is the path to a file which will contain this token in json and will be mounted inside the docker container by Optimus. - -`start` function is using `fetch_config_from_optimus()` to get the date range for which this task executes for an iteration. In this example, configuration is fetched using REST APIs provided by optimus although there are variety of ways to get them. After extracting `API_KEY` from secret file, unmarshalling it to json with ` json.load()` send a http request to nasa api. Response can be parsed and printed using the following function - -```python -def print_details(jd): - """ - Parse and calculate what we need from NASA endpoint response - - :param jd: json data fetched from NASA API - :return: - """ - element_count = jd['element_count'] - potentially_hazardous = [] - for search_date in jd['near_earth_objects'].keys(): - for neo in jd['near_earth_objects'][search_date]: - if neo["is_potentially_hazardous_asteroid"] is True: - potentially_hazardous.append({ - "name": neo["name"], - "estimated_diameter_km": neo["estimated_diameter"]["kilometers"]["estimated_diameter_max"], - "relative_velocity_kmh": neo["close_approach_data"][0]["relative_velocity"]["kilometers_per_hour"] - }) - - print("total tracking: {}\npotential hazardous: {}".format(element_count, len(potentially_hazardous))) - for haz in potentially_hazardous: - print("Name: {}\nEstimated Diameter: {} km\nRelative Velocity: {} km/h\n\n".format( - haz["name"], - haz["estimated_diameter_km"], - haz["relative_velocity_kmh"] - )) - return -``` - - - -Finish it off by adding the main function - -```python -if __name__ == "__main__": - start() -``` - - - -Add `requests` library in `requirements.txt` - -```ini -requests==v2.25.1 -``` - - - -Once the python code is ready, wrap this in a `Dockerfile` - -```dockerfile -# set base image (host OS) -FROM python:3.8 - -# set the working directory in the container -RUN mkdir -p /opt -WORKDIR /opt - -# copy the content of the local src directory to the working directory -COPY task/neo/executor . - -# install dependencies -RUN pip install -r requirements.txt - -CMD ["python3", "main.py"] -``` -### 2a. Creating a yaml plugin -The Yaml implementation is as simple as providing the plugin details as below. -```yaml -name: Neo -description: Near earth object tracker -plugintype: task -pluginversion: latest -image: ghcr.io/kushsharma/optimus-task-neo-executor -secretpath: /tmp/.secrets - -questions: -- name: RANGE_START - prompt: Date range start - help: YYYY-MM-DD format - required: true -- name: RANGE_END - prompt: Date range end - help: YYYY-MM-DD format - required: true -``` -Based on the usecase, additional validation can be added to the questions section. Refer [above](#yaml-implementation-of-plugin) for more usecases and features provided. - -### 2b. Creating a binary plugin - -At the moment Optimus supports task as well as hook plugins. In this section we will be explaining how to write a new task although both are very similar. - -Now that base image is ready for execution, this needs to be scheduled at a fixed interval using `jobs` but for optimus to understand **Neo** task, we need to write an adapter for it. - -#### Implementing plugin interface - -Because we are using Go, start by initialising Go module in `neo` directory as follows - -```go -go mod init github.com/kushsharma/optimus-plugins/task/neo -``` - -Prepare `main.go` in the same directory structure - -``` -.git -/task - /neo - /executor - /main.py - /requirements.txt - /Dockerfile - /main.go - /go.mod - /go.sum -README.md -``` - - - -Start by adding the following boilerplate code - -```go -package main - -import ( - "context" - "errors" - "fmt" - "strconv" - - "github.com/hashicorp/go-hclog" - "github.com/odpf/optimus/internal/models" - "github.com/odpf/optimus/plugin" - "github.com/odpf/optimus/plugin/base" -) - -var ( - Name = "neo" - Version = "latest" - Image = "ghcr.io/kushsharma/optimus-task-neo-executor" -) - -type Neo struct{} - -func main() { - plugin.Serve(func(log hclog.Logger) interface{} { - return &Neo{ - log: log, - } - }) -} -``` - - - -The plugin binary serves a GRPC server on start but before the communication channel is created protocol version, socket, port, and some other metadata needs to be printed as the handshake information to stdout which the core will read. Plugin and core needs to mutually conclude on same protocol version to start the communication. Client side protocol version announcement is done using `HandshakeConfig` provided in `main()`. - -**Handshake contract:** -CORE-PROTOCOL-VERSION | APP-PROTOCOL-VERSION | NETWORK-TYPE | NETWORK-ADDR | PROTOCOL - -**For example:** - -1|1|tcp|127.0.0.1:1234|grpc - -You don't have to worry about this if you are using the provided `plugin.Serve` and all this happens automatically behind the scene. Core will initiate a connection with the plugin server as a client when the core binary boots and caches the connection for further internal use. - -A single binary can serve more than one kind of plugin, in this example stick with just one. Each plugin is composed of one `base` plugin implementation and some additional optional `mods`. - -#### Base Plugin - -Base plugin interface needs to be [implemented](https://github.com/odpf/proton/blob/54e0bec2df4235cabea4ac2127534a468584e932/odpf/optimus/plugins/base.proto) by every plugin. It is responsible for providing plugin metadata to Optimus core. - -```protobuf -syntax = "proto3"; -package odpf.optimus.plugins; - -// Base must be implemented by all plugins -service Base { - // PluginInfo provides basic details for this plugin - rpc PluginInfo(PluginInfoRequest) returns (PluginInfoResponse); -} - -// PluginType enumerates the type of plugins Optimus supports -enum PluginType { - PluginType_UNKNOWN = 0; - PluginType_TASK = 1; - PluginType_HOOK = 2; -} - -// PluginMod enumerates the type of mods this plugin supports -enum PluginMod { - PluginMod_UNKNOWN = 0; - PluginMod_CLI = 1; - PluginMod_DEPENDENCYRESOLVER = 2; -} - -// HookType enumerates the type of hook Optimus supports -enum HookType { - HookType_UNKNOWN = 0; - HookType_PRE = 1; - HookType_POST = 2; - HookType_FAIL = 3; -} - -message PluginInfoRequest {} -message PluginInfoResponse { - string name = 1; - string description = 2; - PluginType plugin_type = 3; - repeated PluginMod plugin_mods = 4; - - // plugin_version is the semver version of this individual plugin - string plugin_version = 5; - // api_versions indicates the versions of the Optimus Plugin API - // this plugin supports - repeated string api_version = 6; - - // docker image including version if this executes a docker image - string image = 10; - - // HOOK specific - // name of hooks on which this should depend on before executing - repeated string depends_on = 20; - HookType hook_type = 21; - - // Experimental - // will be mounted inside the container as volume - string secret_path = 30; -} -``` - -If your plugin simply wants to register itself as task or hook for execution and nothing else then that's it. You don't need to implement anything else but for additional features we can implement plugin `mod`. - -#### Plugin Mods - -Plugin can have none or many plugins mods being implemented at the same time. At the moment there are 2 mods available for usage - -1. [CLIMod](https://github.com/odpf/proton/blob/54e0bec2df4235cabea4ac2127534a468584e932/odpf/optimus/plugins/cli.proto): It provides plugin to interact with Optimus cli. Plugin can provide default configs, ask questions from users to create job specification, override default asset macro compilation behaviour, etc. -2. [DependencyResolverMod](https://github.com/odpf/proton/blob/54e0bec2df4235cabea4ac2127534a468584e932/odpf/optimus/plugins/dependency_resolver.proto): It provides plugin to implement automatic dependency resolution using assets/configs. - -In this example we will use the CLIMod. - -To start serving GRPC, either we write our own implementation for serialising/deserialising Go structs to protobufs or reuse the one already provided by [core](https://github.com/odpf/optimus/blob/eaa50bb37d7e738d9b8a94332312f34b04a7e16b/plugin/task/server.go). Optimus GRPC server accepts an interface which we will implement next on Neo struct. Custom protobuf adapter can also be written using the [provided](https://github.com/odpf/proton/blob/54e0bec2df4235cabea4ac2127534a468584e932/odpf/optimus/plugins/base.proto) protobuf stored in odpf [repository](https://github.com/odpf/proton). - -Add the following code in the existing `main.go` as an implementation to [BasePlugin](https://github.com/odpf/proton/blob/54e0bec2df4235cabea4ac2127534a468584e932/odpf/optimus/plugins/base.proto) - -```go -type Neo struct{} - -// GetSchema provides basic task details -func (n *Neo) PluginInfo() (*models.PluginInfoResponse, error) { - return &models.PluginInfoResponse{ - Name: Name, - Description: "Near earth object tracker", - PluginType: models.PluginTypeTask, - PluginMods: []models.PluginMod{models.ModTypeCLI}, - PluginVersion: Version, - APIVersion: []string{strconv.Itoa(base.ProtocolVersion)}, - - // docker image that will be executed as the actual transformation task - Image: fmt.Sprintf("%s:%s", Image, Version), - }, nil -} -``` - -You might have noticed we have specified at line number 9 that we are supporting `models.ModTypeCLI`. This will let Optimus know what all this plugin is capable of. Let's implement the [CLIMod](https://github.com/odpf/proton/blob/54e0bec2df4235cabea4ac2127534a468584e932/odpf/optimus/plugins/cli.proto) now. - - -```go -// GetQuestions provides questions asked via optimus cli when a new job spec -// is requested to be created -func (n *Neo) GetQuestions(ctx context.Context, req models.GetQuestionsRequest) (*models.GetQuestionsResponse, error) { - tQues := []models.PluginQuestion{ - { - Name: "Start", - Prompt: "Date range start", - Help: "YYYY-MM-DD format", - }, - { - Name: "End", - Prompt: "Date range end", - Help: "YYYY-MM-DD format", - }, - } - return &models.GetQuestionsResponse{ - Questions: tQues, - }, nil -} - -// ValidateQuestion validate how stupid user is -// Each question config in GetQuestions will send a validation request -func (n *Neo) ValidateQuestion(ctx context.Context, req models.ValidateQuestionRequest) (*models.ValidateQuestionResponse, error) { - var err error - switch req.Answer.Question.Name { - case "Start": - err = func(ans interface{}) error { - d, ok := ans.(string) - if !ok || d == "" { - return errors.New("not a valid string") - } - // can choose to parse here for a valid date but we will use optimus - // macros here {{.DSTART}} instead of actual dates - // _, err := time.Parse(time.RFC3339, d) - // return err - return nil - }(req.Answer.Value) - case "End": - err = func(ans interface{}) error { - d, ok := ans.(string) - if !ok || d == "" { - return errors.New("not a valid string") - } - // can choose to parse here for a valid date but we will use optimus - // macros here {{.DEND}} instead of actual dates - // _, err := time.Parse(time.RFC3339, d) - // return err - return nil - }(req.Answer.Value) - } - if err != nil { - return &models.ValidateQuestionResponse{ - Success: false, - Error: err.Error(), - }, nil - } - return &models.ValidateQuestionResponse{ - Success: true, - }, nil -} - -func findAnswerByName(name string, answers []models.PluginAnswer) (models.PluginAnswer, bool) { - for _, ans := range answers { - if ans.Question.Name == name { - return ans, true - } - } - return models.PluginAnswer{}, false -} - -// DefaultConfig are a set of key value pair which will be embedded in job -// specification. These configs can be requested by the docker container before -// execution -// PluginConfig Value can contain valid go templates and they will be parsed at -// runtime -func (n *Neo) DefaultConfig(ctx context.Context, request models.DefaultConfigRequest) (*models.DefaultConfigResponse, error) { - start, _ := findAnswerByName("Start", request.Answers) - end, _ := findAnswerByName("End", request.Answers) - - conf := []models.PluginConfig{ - { - Name: "RANGE_START", - Value: start.Value, - }, - { - Name: "RANGE_END", - Value: end.Value, - }, - } - return &models.DefaultConfigResponse{ - Config: conf, - }, nil -} - -// DefaultAssets are a set of files which will be embedded in job -// specification in assets folder. These configs can be requested by the -// docker container before execution. -func (n *Neo) DefaultAssets(ctx context.Context, _ models.DefaultAssetsRequest) (*models.DefaultAssetsResponse, error) { - return &models.DefaultAssetsResponse{}, nil -} - -// override the compilation behaviour of assets - if needed -func (n *Neo) CompileAssets(ctx context.Context, req models.CompileAssetsRequest) (*models.CompileAssetsResponse, error) { - return &models.CompileAssetsResponse{ - Assets: req.Assets, - }, nil -} -``` - - - -All the functions are prefixed with comments to give you basic idea of what each one is doing, for advanced usage, look at other plugins used in the wild. - -Few things to note: - -- `PluginInfo` is used to define a unique `name` used by your plugin to identify yourself, keep it simple. -- `PluginInfo` contains `Image` field that specify the docker image which Optimus will execute when needed. This is where the neo python image will go. -- `Version` field can be injected using build system, here we are only keeping a default value. -- `PluginType` in `PluginInfo` will tell of this plugin should be read as `Task` or `Hook` by Optimus core. - - - -### Building everything - -Once the code is ready, to build there is a pretty nice tool available for golang [goreleaser](https://github.com/goreleaser/goreleaser/). A single configuration file will contain everything to build the docker image as well as the binary. - -Put this in the root of the project as `.goreleaser.yml` - -```yaml -builds: - - dir: ./task/neo - main: . - id: "neo" - binary: "optimus-neo_{{.Os}}_{{.Arch}}" - ldflags: - - -s -w -X main.Version={{.Version}} -X main.Image=ghcr.io/kushsharma/optimus-task-neo-executor - goos: - - linux - - darwin - - windows - goarch: - - amd64 - - arm64 - env: - - CGO_ENABLED=0 -archives: - - replacements: - darwin: darwin - linux: linux - windows: windows - amd64: amd64 - format_overrides: - - goos: windows - format: zip -release: - prerelease: auto -checksum: - name_template: 'checksums.txt' -snapshot: - name_template: "{{ .Tag }}-next" -changelog: - sort: asc - filters: - exclude: - - '^docs:' - - '^test:' -dockers: - - - goos: linux - goarch: amd64 - image_templates: - - "ghcr.io/kushsharma/optimus-task-neo-executor:latest" - - "ghcr.io/kushsharma/optimus-task-neo-executor:{{ .Version }}" - dockerfile: ./task/neo/executor/Dockerfile - extra_files: - - task/neo/executor - -brews: - - name: optimus-plugins-kush - install: | - bin.install Dir["optimus-*"] - tap: - owner: kushsharma - name: homebrew-taps - license: "Apache 2.0" - description: "Optimus plugins - [Optimus Near earth orbit tracker]" - commit_author: - name: Kush Sharma - email: 3647166+kushsharma@users.noreply.github.com -``` - -Please go through goreleaser documentation to understand what this config is doing but just to explain briefly - -- It will build golang task plugin adapter for multiple platforms, archives them and release on Github -- Build and push the docker image for the python neo task -- Create a Formula for installing this plugin on Mac using brew -- Each plugin will follow the binary naming convention as `optimus-__`. For example: `optimus-bq2bq_linux_amd64` - -Once installed, run goreleaser using - -```shell -goreleaser --snapshot --rm-dist -``` - -Use this [repository](https://github.com/kushsharma/optimus-plugins) as an example to see how everything fits in together. It uses github workflows to run goreleaser and publish everything. - -## How to use - -### Installing a plugin - -Plugins need to be installed in Optimus server before it can be used. Optimus uses following directories for discovering plugin binaries -``` -./.plugins -./ -/ -/.optimus/plugins -$HOME/.optimus/plugins -/usr/bin -/usr/local/bin -``` -Even though the above list of directories are involved in plugin discovery, it is advised to use `.plugins` in the current working directory of the project or optimus binary. - -### Server-side installation -On Server side, plugins (both binary and yaml) can be installed declaratively at runtime by listing the plugin artifacts in plugins section of server config. -```bash -optimus plugin install -c config.yaml # This will install plugins in `.plugins` folder. -``` -Plugin section to be added in server config: -```yaml -plugin: - artifacts: - - https://...path/to/optimus-plugin-neo.yaml # http - - http://.../plugins.zip # zip - - ../transformers/optimus-bq2bq_darwin_arm64 # relative paths - - ../transformers/optimus-plugin-neo.yaml -``` -### Project-side installation -On the project side, where optimus cli is used to generate specifications or deployment, optimus cli can sync the yaml plugins supported-and-served by optimus server (with host as declared in project config). - -```bash -optimus plugin sync -c optimus.yaml # This will install plugins in `.plugins` folder. -``` - -### Using in job specification - -Once everything is built and in place, we can generate job specifications that uses **neo** as the task type. - -```shell -optimus create job -? What is the job name? is_last_day_on_earth -? Who is the owner of this job? kush.sharma@example.io -? Which task to run? neo -? Specify the start date 2021-05-25 -? Specify the interval (in crontab notation) 0 2 * * * -? Transformation window daily -? Date range start {{.DSTART}} -? Date range end {{.DEND}} -job created successfully is_last_day_on_earth -``` - -Create a commit and deploy this specification if you are using optimus with a git managed repositories or send a REST call or use GRPC, whatever floats your boat. - -### Checking the output - -If your optimus deployment is using Airflow as the scheduling engine, open the task log and verify something like this - -``` -total tracking: 14 -potential hazardous: 1 -Name: (2014 KP4) -Estimated Diameter: 0.8204270649 km -Relative Velocity: 147052.9914506647 km/h -``` - -## Additional details - -A task is like a pipeline, it takes some input, it runs a procedure on the input and then produces an output. Procedure is wrapped inside the docker image, output is owned by the procedure which could be anything but input should be injected somehow by optimus or at least provide some information about where/what input is. Currently, Optimus supports two kind of inputs: - -- Key value configuration -- File assets - -##### Task Configuration - -Task configurations are key value pair provided as part of job specification in `job.yaml` file. These are based on plugin implementation of `TaskPlugin` interface. These configurations accept simple strings as well as Optimus [macros](../concepts/overview.md#Macros-&-Templates). There are few Optimus provided configuration to all tasks and hooks even if users don't specifically provide them: - -- DSTART -- DEND -- EXECUTION_TIME - -Secrets can be used in the task configuration by using macros like ` SECRET_PATH: "{{.secret.api_key_path}}" `. -Where `api_key_path` is a value stored inside secrets. This configuration in macros will be replaced by optimus serer while sending a request to the plugin with actual value and can be used by the plugin. In the executor of the plugin, this configuration will be made available as environment variable. - -##### File Assets - -Sometimes a task may need more than just key value configuration, this is where assets can be used. Assets are packed along with the job specification and should have unique names. A task can have more than one asset file but if any file name conflicts with any already existing plugin in the optimus, it will throw an error, so it is advised to either prefix them or name them very specific to the task. These assets should ideally be small and not more than ~5 MB and any heavy lifting if required should be done directly inside the task container. - -### Requesting task context - -Optimus calls these task configuration and asset inputs for each scheduled execution of a task as `context`. There are variety of ways to fetch task context from optimus. - -- REST API -- GRPC function call -- Optimus cli - -##### REST API - -This is probably the easiest way using [REST API](https://github.com/odpf/optimus/blob/0ab5a4d44a7b2b85e9a160aef3648d8ba798536a/third_party/OpenAPI/odpf/optimus/runtime_service.swagger.json#L187) provided by optimus server. Each container when boots up has few pre-defined environment variables injected by optimus, few of them are: - -- JOB_NAME -- OPTIMUS_HOSTNAME -- JOB_DIR -- PROJECT -- SCHEDULED_AT -- INSTANCE_TYPE -- INSTANCE_NAME - -These variables might be needed to make the call and in response, container should get configuration and files as key value pairs in json. - -##### GRPC call - -Plugin can choose to make a GRPC call using `RegisterInstance` [function](https://github.com/odpf/proton/blob/main/odpf/optimus/runtime_service.proto#L124) and should get the context back in return. - -##### Optimus cli - -There could be scenarios where it is not possible or maybe not convenient to modify the base execution image and still task need context configuration values. One easy way to do this is by wrapping the base docker image into another docker image and using optimus binary to request task context. Optimus command will internally send a GRPC call and store the output in `${JOB_DIR}/in/` directory. It will create one `.env` file containing all the configurations, one `.secret` file with environment variables with potentially sensitive values, and all the asset files belong to the provided task. Optimus command can be invoked as - -```shell -OPTIMUS_ADMIN_ENABLED=1 /opt/optimus admin build instance $JOB_NAME --project $PROJECT --output-dir $JOB_DIR --type $INSTANCE_TYPE --name $INSTANCE_NAME --scheduled-at $SCHEDULED_AT --host $OPTIMUS_HOSTNAME -``` - -You might have noticed, optimus need OPTIMUS_ADMIN_ENABLED as env variable to enable admin commands. An example of wrapper `Dockerfile` is as follows - -```dockerfile -FROM ghcr.io/kushsharma/optimus-task-neo-executor:latest - -# path to optimus release tar.gz -ARG OPTIMUS_RELEASE_URL - -RUN apt install curl tar -y -RUN mkdir -p /opt -RUN curl -sL ${OPTIMUS_RELEASE_URL} | tar xvz optimus -RUN mv optimus /opt/optimus || true -RUN chmod +x /opt/optimus - -# or copy like this -COPY task/neo/example.entrypoint.sh /opt/entrypoint.sh -RUN chmod +x /opt/entrypoint.sh - -ENTRYPOINT ["/opt/entrypoint.sh"] -CMD ["python3", "/opt/main.py"] -``` - -Where `entrypoint.sh` is as follows - -```shell -#!/bin/sh -# wait for few seconds to prepare scheduler for the run -sleep 5 - -# get resources -echo "-- initializing optimus assets" -OPTIMUS_ADMIN_ENABLED=1 /opt/optimus admin build instance $JOB_NAME --project $PROJECT --output-dir $JOB_DIR --type $TASK_TYPE --name $TASK_NAME --scheduled-at $SCHEDULED_AT --host $OPTIMUS_HOSTNAME - -# TODO: this doesnt support using back quote sign in env vars -echo "-- exporting env" -set -o allexport -source $JOB_DIR/in/.env -set +o allexport - -echo "-- current envs" -printenv - -echo "-- exporting secret envs" -set -o allexport -source $JOB_DIR/in/.secret -set +o allexport - -echo "-- running unit" -exec $(eval echo "$@") -``` - -All of it can be built using goreleaser as well - -```yaml -dockers: - - - goos: linux - goarch: amd64 - image_templates: - - "ghcr.io/kushsharma/optimus-task-neo:latest" - - "ghcr.io/kushsharma/optimus-task-neo:{{ .Version }}" - dockerfile: ./task/neo/example.Dockerfile - extra_files: - - task/neo/example.entrypoint.sh - build_flag_templates: - - "--build-arg=OPTIMUS_RELEASE_URL=https://github.com/odpf/optimus/releases/download/v0.0.1-rc.2/optimus_0.0.1-rc.2_linux_x86_64.tar.gz" -``` - -Keep in mind, the plugin binary now needs to point to this `optimus-task-neo` docker image and not the base one. An example of this approach can be checked in the provided [repository](https://github.com/kushsharma/optimus-plugins). - -### Directory Structure - -You might have already understood it by now but still just to state, the reason we went ahead with the provided directory structure earlier so that we can support more than one task and even hooks if we need to in the same repository. Image a single repository of plugins as an organization repository where one can find all that can be contributed by an entity - -``` -/task - /neo - /executor - /main.py - /requirements.txt - /Dockerfile - /main.go - /go.mod - /go.sum - /task-2 - /task-3 -/hook - /hook-1 - /hook-2 -.goreleaser.yml -README.md -``` - -### Secret management - -You must be wondering from where that api token came from when we said it will be mounted inside the container. Optimus need to somehow know what the secret is, for this current implementation of optimus relies on Kubernetes [Secrets](https://kubernetes.io/docs/concepts/configuration/secret/). Optimus is built to be deployed on kubernetes, although it can work just fine without it as well but might need some tweaking here and there. An example of creating this secret - -```yaml -apiVersion: v1 -kind: Secret -metadata: - name: optimus-task-neo -type: Opaque -data: - key.json: base64encodedAPIkeygoes -``` - -Notice the name of the secret `optimus-task-neo` which is actually based on a convention. That is if secret is defined, Optimus will look in kubernetes using `optimus-task-` as the secret name and mount it to the path provided in `SecretPath` field of `PluginInfo`. - -There is also a new way to using secrets in the job for a plugin or otherwise, we can store a user defined secret in the optimus server with the following command -```shell -optimus secret set secret_name -``` -Verify if the secret is registered properly with optimus server using following command -```shell -optimus secret list -``` -It should list the secret registered above. - -Then we can mention the secret in the job spec configuration like mentioned in the Task Configuration section. The configuration with secrets will be made available to the calls to plugins on dependency mod, and set as environment variable in the executor used by the scheduler. - diff --git a/docs/docs/getting-started/configuration.md b/docs/docs/getting-started/configuration.md deleted file mode 100644 index 4ffd57db42..0000000000 --- a/docs/docs/getting-started/configuration.md +++ /dev/null @@ -1,124 +0,0 @@ ---- -id: configuration -title: Configurations ---- - -## Client Configuration - -### Initialization - -Client configuration can be initialized using Optimus. -To do so, use Optimus `init` command. -An interactive questionaire will be presented, such as below: - -```zsh -$ optimus init - -? What is the Optimus service host? [? for help] localhost:9100 -? What is the Optimus project name? local-project -? What is the namespace name? namespace1 -? What is the type of data store for this namespace? bigquery -? Do you want to add another namespace? No -Client config is initialized successfully -If you want to modify, go to [optimus.yaml] -``` - -After running the `init` command, Optimus client config will be configured. -Along with it is the directories for the chosen namespaces. -For example, since the above example only specifies _namespace1_, then directory _namespace1_ will be created along with `optimus.yaml` client config. - -The user can optionally update the `optimus.yaml` file if necessary. -For clearer example, see client configuration example on `optimus.sample.yaml` - -### Usage - -Optimus client configuration can be loaded from file (use `--config` flag), or `optimus.yaml` file in current working directory where the Optimus command is executed. - ---- -**1. Using --config flag** - -```sh -$ optimus deploy --config /path/to/config/file.yaml -``` - ---- -**2. Using default optimus.yaml file** - -```sh -$ tree -. # current project structure -├── namespace-1 -│   └── jobs -│   └── resources -├── namespace-2 -│   └── jobs -│   └── resources -└── optimus.yaml # use this file -$ optimus deploy -``` - ---- - -If both `--config` flag and `optimus.yaml` do exist, then the one will be used is the file defined in `--config` flag. - -## Server Configuration - -See server configuration example on `config.sample.yaml` - -Optimus server configuration can be loaded from file (use `--config` flag), environment variable `OPTIMUS_`, or `config.yaml` file in executable directory. - ---- -**1. Using --config flag** -```sh -$ optimus serve --config /path/to/config/file.yaml -``` - ---- -**2. Using environment variable** - -All the configs can be passed as environment variables using `OPTIMUS_` convention. `` is the key name of config using `_` as the path delimiter to concatenate between keys. - -For example, to use environment variable, assuming the following configuration layout: - -```yaml -version: 1 -serve: - port: 9100 - app_key: randomhash -``` - -Here is the corresponding environment variable for the above - -Configuration key | Environment variable | -------------------|----------------------| -version | OPTIMUS_VERSION | -serve.port | OPTIMUS_PORT | -serve.app_key | OPTIMUS_SERVE_APP_KEY| - -Set the env variable using export -```sh -$ export OPTIMUS_PORT=9100 -``` - ---- -**3. Using default config.yaml from executable binary directory** -```sh -$ which optimus -/usr/local/bin/optimus -``` - -So the `config.yaml` file can be loaded on `/usr/local/bin/config.yaml` - ---- - -If user specify configuration file using `--config` flag, then any configs defined in env variable and default config.yaml from exec directory will not be loaded. - -If user specify the env variable and user also have config.yaml from exec directory, then any key configs from env variable will override the key configs defined in config.yaml from exec directory. - ---- - -App key is used to encrypt credentials and can be randomly generated using -```shell -head -c 50 /dev/random | base64 -``` -Just take the first 32 characters of the string. \ No newline at end of file diff --git a/docs/docs/getting-started/installation.md b/docs/docs/getting-started/installation.md index 9fce6bf586..69f760c40a 100644 --- a/docs/docs/getting-started/installation.md +++ b/docs/docs/getting-started/installation.md @@ -1,58 +1,66 @@ # Installation -Installing Optimus on any system is straight forward. We provide pre-built [binaries](https://github.com/odpf/optimus/releases), -Docker Images and support package managers. +Installing Optimus on any system is straight forward. There are several approaches to install Optimus: -## MacOS +- Using a pre-built binary +- Installing with package manager +- Installing with Docker +- Installing from source -You can install Optimus using homebrew on macOS: +## Using a Pre-built Binary + +The client and server binaries are downloadable at the [releases](https://github.com/raystack/optimus/releases) section. + +Once installed, you should be able to run: ```shell -brew install odpf/tap/optimus -optimus version +$ optimus version ``` -## Download Binaries +## Installing with Package Manager -The client and server binaries are downloadable at the releases tab. There is -currently no installer available. You have to add the Optimus binary to the PATH -environment variable yourself or put the binary in a location that is already -in your $PATH (e.g. /usr/local/bin, ...). +For macOS, you can install Optimus using homebrew: -Once installed, you should be able to run: +```shell +$ brew install raystack/tap/optimus +$ optimus version +``` + +## Installing using Docker + +To pull latest image: ```shell -optimus version +$ docker pull raystack/optimus:latest ``` -## Compiling from source +To pull specific image: + +```shell +$ docker pull raystack/optimus:0.6.0 +``` + +## Installing from Source ### Prerequisites Optimus requires the following dependencies: -- Golang (version 1.16 or above) +- Golang (version 1.18 or above) - Git ### Build -Run the following commands to compile `optimus` from source +Run the following commands to compile optimus from source ```shell -git clone git@github.com:odpf/optimus.git -cd optimus -make build +$ git clone git@github.com:raystack/optimus.git +$ cd optimus +$ make build ``` Use the following command to test ```shell -./optimus version -``` - -Optimus service can be started with the following command although there are few required -[configurations](./configuration.md) for it to start. - -```shell -./optimus serve +$ optimus version ``` diff --git a/docs/docs/getting-started/quick-start.md b/docs/docs/getting-started/quick-start.md new file mode 100644 index 0000000000..aca34b2394 --- /dev/null +++ b/docs/docs/getting-started/quick-start.md @@ -0,0 +1,344 @@ +# Quickstart + +This quick start will guide you to try out Optimus fast without getting into many details. As part of this, you will +be provided with step-by-step instructions to start Optimus server, connect Optimus client with server, create +BigQuery resource through Optimus, create BigQuery to BigQuery job, and deploy it. + +## Prerequisite + +- Docker or a local installation of Optimus. +- [Postgres](https://www.postgresql.org/download/) database. +- BigQuery project +- [Airflow](https://airflow.apache.org/docs/apache-airflow/stable/installation/index.html) + - This is not mandatory to complete the quick start, but needed for scheduling jobs. + +## Step 1: Start Server + +Start server with Raystack’s BigQuery to BigQuery [plugin](https://github.com/raystack/transformers). + +Create a config.yaml file: + +```yaml +version: 1 + +log: + level: debug + +serve: + port: 9100 + host: localhost + ingress_host: localhost:9100 + app_key: + db: + dsn: postgres://@localhost:5432/dbname?sslmode=disable + +plugin: + artifacts: + - https://github.com/raystack/transformers/releases/download/v0.2.1/transformers_0.2.1_macos_x86_64.tar.gz +``` + +_Note: make sure you put artifacts link that suitable to your system._ + +### Start Server + +With the config.yaml available in the same working directory, you can start server by running: + +```shell +$ optimus serve --install-plugins +``` + +This will automatically install the plugins as specified in your server configuration. + +## Step 2: Connect Client With Server + +Go to the directory where you want to have your Optimus specifications. Create client configuration by using +optimus `init` command. An interactive questionnaire will be presented, such as below: + +```shell +$ optimus init + +? What is the Optimus service host? localhost:9100 +? What is the Optimus project name? sample_project +? What is the namespace name? sample_namespace +? What is the type of data store for this namespace? bigquery +? Do you want to add another namespace? No +Client config is initialized successfully +``` + +After running the init command, Optimus client config will be configured. Along with it, the directories for the chosen +namespaces, including the subdirectories for jobs and resources will be created with the following structure: + +``` +sample_project +├── sample_namespace +│ └── jobs +│ └── resources +└── optimus.yaml +``` + +Below is the client configuration that has been generated: + +```yaml +version: 1 +log: + level: INFO + format: "" +host: localhost:9100 +project: + name: sample_project + config: {} +namespaces: + - name: sample_namespace + config: {} + job: + path: sample_namespace/jobs + datastore: + - type: bigquery + path: sample_namespace/resources + backup: {} +``` + +Let’s add `storage_path` project configuration that is needed to store the result of job compilation and +`scheduler_host` which is needed for compilation. + +```yaml +project: + name: sample_project + config: + storage_path: file:///Users/sample_user/optimus/sample_project/compiled + scheduler_host: http://sample-host +``` + +_Note: storage path is the location where airflow is reading its dags from._ + +Now, let's register `sample_project` and `sample_namespace` to your Optimus server. + +```shell +$ optimus project register --with-namespaces +``` + +You can verify if the project has been registered successfully by running this command: + +```shell +$ optimus project describe +``` + +## Step 3: Create BigQuery resource + +Before creating BigQuery resources, make sure your Optimus server has access to your BQ project by adding a +`BQ_SERVICE_ACCOUNT` secret. + +Assume you have your service account json file in the same directory (project directory), create the secret using the +following command. Make sure the service account that you are using is authorized to create tables. + +```shell +$ optimus secret set BQ_SERVICE_ACCOUNT --file service_account.json +``` + +Check whether the secret has been registered successfully by running this command. + +```shell +$ optimus secret list +``` + +Now, let’s create a resource using the following interactive command. + +```shell +$ optimus resource create + +? Please choose the namespace: sample_namespace +? What is the resource name? sample-project.sample_namespace.table1 +? What is the resource type? table +? Provide new directory name to create for this spec? [sample_namespace/resources] sample-project.sample_namespace.table1 + +Resource spec [sample-project.sample_namespace.table1] is created successfully +``` + +_Note: resource name should be unique within the project. Take a look at the complete guide on how to create resource +[here](../client-guide/manage-bigquery-resource.md) if needed._ + +After running the command, the resource specification file will be automatically created in the following directory: + +``` +sample_project +├── sample_namespace +│ └── jobs +│ └── resources +| └── sample-project.sample_namespace.table1 +| └── resource.yaml +└── optimus.yaml +``` + +Let’s open the resource.yaml file and add additional spec details as follows: + +```yaml +version: 1 +name: sample-project.sample_namespace.table1 +type: table +labels: {} +spec: + description: "sample optimus quick start table" + schema: + - name: sample_day + type: STRING + mode: NULLABLE + - name: sample_timestamp + type: TIMESTAMP + mode: NULLABLE +``` + +Now that resource specification is complete, let’s deploy this to the Optimus server and it will create the resource +in BigQuery. + +```shell +$ optimus resource upload-all --verbose + +> Validating namespaces +namespace validation finished! + +> Uploading all resources for namespaces [sample_namespace] +> Deploying bigquery resources for namespace [sample_namespace] +> Receiving responses: +[success] sample-project.sample_namespace.table1 +resources with namespace [sample_namespace] are deployed successfully +finished uploading resource specifications to server! +``` + +## Step 4: Create & Deploy Job + +Sync plugins to your local for optimus to provide an interactive UI to add jobs, this is a prerequisite before +creating any jobs. + +```shell +$ optimus plugin sync +``` + +Let’s verify if the plugin has been synced properly by running below command. + +```shell +$ optimus version +``` + +You should find `bq2bq` plugin in the list of discovered plugins. + +To create a job, we need to provide a job specification. Let’s create one using the interactive optimus job command. + +```shell +$ optimus job create +? Please choose the namespace: sample_namespace +? Provide new directory name to create for this spec? [.] sample-project.sample_namespace.table1 +? What is the job name? sample-project.sample_namespace.table1 +? Who is the owner of this job? sample_owner +? Select task to run? bq2bq +? Specify the schedule start date 2023-01-26 +? Specify the schedule interval (in crontab notation) 0 2 * * * +? Transformation window daily +? Project ID sample-project +? Dataset Name sample_namespace +? Table ID table1 +? Load method to use on destination REPLACE +Job successfully created at sample-project.sample_namespace.table1 +``` + +_Note: take a look at the details of job creation [here](../client-guide/create-job-specifications.md)._ + +After running the job create command, the job specification file and assets directory are created in the following directory. + +``` +├── sample_namespace +│ └── jobs +| └── sample-project.sample_namespace.table1 +| └── assets +| └── query.sql +| └── job.yaml +│ └── resources +| └── sample-project.sample_namespace.table1 +| └── resource.yaml +└── optimus.yaml +``` + +For BQ2BQ job, the core transformation logic lies in `assets/query.sql`. Let’s modify the query to the following script: + +```sql +SELECT +FORMAT_DATE('%A', CAST("{{ .DSTART }}" AS TIMESTAMP)) AS `sample_day`, +CAST("{{ .DSTART }}" AS TIMESTAMP) AS `sample_timestamp`; +``` + +_Note: take a look at Optimus’ supported macros [here](../concepts/macros.md)._ + +Let’s also verify the generated job.yaml file. + +```yaml +version: 1 +name: sample-project.sample_namespace.table1 +owner: sample_owner +schedule: + start_date: "2023-01-26" + interval: 0 2 * * * +behavior: + depends_on_past: false +task: + name: bq2bq + config: + DATASET: sample_namespace + LOAD_METHOD: REPLACE + PROJECT: sample-project + SQL_TYPE: STANDARD + TABLE: table1 +window: + size: 24h + offset: "0" + truncate_to: d +labels: + orchestrator: optimus +hooks: [] +dependencies: [] +``` + +For this quick start, we are not adding any hooks, dependencies, or alert configurations. Take a look at the details +of job specification and the possible options [here](../client-guide/create-job-specifications.md#understanding-the-job-specifications). + +Before proceeding, let’s add the BQ_SERVICE_ACCOUNT secret in the task configuration. + +```yaml +task: + name: bq2bq + config: + BQ_SERVICE_ACCOUNT: "{{.secret.BQ_SERVICE_ACCOUNT}}" + DATASET: sample_namespace +``` + +Later, you can avoid having the secret specified in every single job specification by adding it in the parent yaml +specification instead. For more details, you can take a look [here](../client-guide/organizing-specifications.md). + +Now the job specification has been prepared, lets try to add it to the server by running this command: + +```shell +$ optimus job replace-all --verbose + +> Validating namespaces +validation finished! + +> Replacing all jobs for namespaces [sample_namespace] +> Receiving responses: +[sample_namespace] received 1 job specs +[sample_namespace] found 1 new, 0 modified, and 0 deleted job specs +[sample_namespace] processing job job1 +[sample_namespace] successfully added 1 jobs +replace all job specifications finished! +``` + +Above command will try to add/modify all job specifications found in your project. We are not providing registering +a single job through Optimus CLI, but it is possible to do so using API. + +Now that the jobs has been registered to Optimus, let’s compile and upload it to the scheduler by using the following command. + +```shell +$ optimus scheduler upload-all +``` + +The command will try to compile your job specification to the DAG file. The result will be stored in the `storage_path` +location as you have specified when configuring the optimus.yaml file. + +Later, once you have Airflow ready and want to try out, this directory can be used as a source to be scheduled by Airflow. diff --git a/docs/docs/guides/adding-hook.md b/docs/docs/guides/adding-hook.md deleted file mode 100644 index f2113d9fb0..0000000000 --- a/docs/docs/guides/adding-hook.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -id: adding-hook -title: Adding hook to a Job ---- - -There might be a certain operations that you might want to run before or after the Job. -Please go through [concepts](../concepts/overview.md) to know more about it. - -In order to add a hook to an existing Job, run the following command and answer the -corresponding prompts: - -``` -$ ./optimus job addhook -? Select a Job example_job -? Which hook to run? transporter -? Filter expression for extracting transformation rows? event_timestamp >= '{{.DSTART}}' - AND event_timestamp < '{{.DEND}}' -``` - -With the above prompt, we're adding the *transporter* hook *post* the execution of -primary job. Filter expression configuration is specific to a transporter hook, -and it might be different for other hooks. - -After this, existing job.yaml file will get updated with the new hook config, and -the job specification would look like: - -```yaml -version: 1 -name: example_job -owner: example@example.com -description: example job to demonstrate hook -schedule: - start_date: "2021-02-18" - interval: 0 3 * * * -behavior: - depends_on_past: false - catch_up: true -task: - name: bq2bq - config: - DATASET: data - LOAD_METHOD: APPEND - PROJECT: example - SQL_TYPE: STANDARD - TABLE: hello_table - window: - size: 24h - offset: "0" - truncate_to: d -labels: - orchestrator: optimus -dependencies: [] -hooks: -- name: transporter - config: - BQ_DATASET: '{{.TASK__DATASET}}' # inherited from task configs - BQ_PROJECT: '{{.TASK__PROJECT}}' - BQ_TABLE: '{{.TASK__TABLE}}' - FILTER_EXPRESSION: 'event_timestamp >= "{{.DSTART}}" AND event_timestamp < "{{.DEND}}"' - KAFKA_TOPIC: optimus_example-data-hello_table - PRODUCER_CONFIG_BOOTSTRAP_SERVERS: '{{.GLOBAL__TRANSPORTER_KAFKA_BROKERS}}' - PROTO_SCHEMA: example.data.HelloTable - STENCIL_URL: '{{.GLOBAL__TRANSPORTER_KAFKA_BROKERS}}' # will be defined as global config -``` - -Now to finish this, create a commit and push changes to target repository. -The gitlab pipeline is idempotent and hence Optimus will handle the new -specifications accordingly. \ No newline at end of file diff --git a/docs/docs/guides/alerts.md b/docs/docs/guides/alerts.md deleted file mode 100644 index 204af28df8..0000000000 --- a/docs/docs/guides/alerts.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -id: alerts -title: Alerts ---- - -User needs to be alerted on specific events on jobs: -* There are two specific events on which a users can configure alerts as of today, `sla_miss` and `failure` -* Add the below sample configuration in your jobspec to configure the alerts. - -Sample configuration -```yaml -behavior: - notify: - - 'on': failure/sla_miss - config : - duration : 2h45m - channels: - - slack://#slack-channel or @team-group or user&gmail.com - - pagerduty://#pagerduty_service_name -``` - -* sla_miss expects a duration config expect users to provide the duration as string. -* slack alerting is supported now which users can configure to channel or team handle or a specific user. -* for pagerduty alerts to work, ```notify_``` secret with pagerduty integration key/routing key needs to be registered with optimus. diff --git a/docs/docs/guides/backup.md b/docs/docs/guides/backup.md deleted file mode 100644 index efa9c85bb8..0000000000 --- a/docs/docs/guides/backup.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -id: backup -title: Backup Resources ---- - -Backup is a common prerequisite step to be done before re-running or modifying a resource. Currently, Optimus supports -backup for BigQuery tables and provides dependency resolution, so backup can be also done to all the downstream tables -as long as it is registered in Optimus and within the same project. - -## Configuring backup details - -Several configurations can be set to have the backup result in your project as your preference. Here are the -available configurations for BigQuery datastore. - -Configuration key | Description | Default | -------------------|------------------------------------------|----------------| -ttl | Time to live in duration | 720h | -prefix | Prefix of the result table name | backup | -dataset | Where the table result should be located | optimus_backup | - -These values can be set in the project [configuration](../getting-started/configuration.md). - - -## Run a backup - -To start a backup, run the following command: - -```shell -$ optimus backup create --resource "resource_name" --project sample-project --namespace sample-namespace -``` - -After you run the command, prompts will be shown. You will need to answer the questions. - -``` -$ optimus backup create --resource "resource_name" --project sample-project --namespace sample-namespace -? Select supported datastore? bigquery -? Why is this backup needed? backfill due to business logic change -? Backup downstream? Yes -``` - -You will be shown a list of resources that will be backed up, including the downstream resources (if you chose to do so). -You can confirm to proceed if the list is as expected, and please wait until the backup is finished. - -Once the backup is finished, the list of backup results along with where it is located will be shown. - - -## Get list of backups - -List of recent backups of a project can be checked using this sub command: - -```shell -$ optimus backup list --project sample-project -``` - -Recent backup ID including the resource, when it was created, what is the description or purpose of the backup will be -shown. Backup ID is used as a postfix in backup result name, thus you can find those results in the datastore -(for example BigQuery) using the backup ID. However, keep in mind that these backup results have expiry time set. - -## Run a backup dry run - -A dry run is also available to simulate all the resources that can be backed up without actually doing it. Example of dry -run usage: - -```shell -$ optimus backup create --resource "resource_name" --project sample-project --namespace sample-namespace --dry-run -``` diff --git a/docs/docs/guides/create-bigquery-dataset.md b/docs/docs/guides/create-bigquery-dataset.md deleted file mode 100644 index 3c74a16108..0000000000 --- a/docs/docs/guides/create-bigquery-dataset.md +++ /dev/null @@ -1,90 +0,0 @@ ---- -id: create-bigquery-dataset -title: Create bigquery dataset ---- - -A dataset is contained within a specific Google project. Datasets are top-level -containers that are used to organize and control access to your tables and views. -A table or view must belong to a dataset, so you need to create at least one. - -There are 3 ways to create a dataset: - -### Creating dataset with Optimus - -Supported datastore can be selected by calling -```bash -optimus resource create -``` -Optimus will request a resource name which should be unique across whole datastore. -All resource specification contains a name field which conforms to a fixed format. -In case of bigquery dataset, format should be -`projectname.datasetname`. -After the name is provided, `optimus` will create a file in configured datastore -directory. Open the created specification file and add additional spec details -as follows: -```yaml -version: 1 -name: temporary-project.optimus-playground -type: dataset -labels: - usage: testdataset - owner: optimus -spec: - description: "example description" - table_expiration: 24 # in hours -``` -This will add labels, description and default table expiration(in hours) to dataset -once the `deploy` command is invoked. - -### Creating dataset over REST - -Optimus exposes Create/Update rest APIS -``` -Create: POST /api/v1beta1/project/{project_name}/namespace/{namespace}/datastore/{datastore_name}/resource -Update: PUT /api/v1beta1/project/{project_name}/namespace/{namespace}/datastore/{datastore_name}/resource -Read: GET /api/v1beta1/project/{project_name}/namespace/{namespace}/datastore/{datastore_name}/resource/{resource_name} -``` - -```json -{ - "resource": { - "version": 1, - "name" : "temporary-project.optimus-playground", - "datastore" : "bigquery", - "type" : "dataset", - "labels": { - "usage": "testdataset", - "owner": "optimus" - }, - "spec" : { - "description": "example description", - "table_expiration": 24 - } - } -} -``` - -### Creating dataset over GRPC - -Optimus in RuntimeService exposes an RPC -```protobuf -rpc CreateResource(CreateResourceRequest) returns (CreateResourceResponse) {} - -message CreateResourceRequest { - string project_name = 1; - string datastore_name = 2; - ResourceSpecification resource = 3; - string namespace = 4; -} -``` -Function payload should be self-explanatory other than the struct `spec` part which -is very similar to how json representation look. - -Spec will use `structpb` struct created with `map[string]interface{}` -For example: -```go -map[string]interface{ - "description": "example description", - "table_expiration": 24 -} -``` \ No newline at end of file diff --git a/docs/docs/guides/create-bigquery-external-table.md b/docs/docs/guides/create-bigquery-external-table.md deleted file mode 100644 index 36993be5d9..0000000000 --- a/docs/docs/guides/create-bigquery-external-table.md +++ /dev/null @@ -1,161 +0,0 @@ ---- -id: create-bigquery-external-table -title: Create bigquery external table ---- - -A BigQuery external table is a data source stored in external storage that you can query directly -in BigQuery the same way you query a table. You can specify the schema of the external table when -it is created. At the moment only Google Drive source with Google Sheets format is supported. - -There are 3 ways to create an external table: - -### Creating external table with Optimus - -Supported datastore can be selected by calling - -```bash -optimus resource create -``` - -Optimus will request a resource name which should be unique across whole datastore. -All resource specification contains a name field which conforms to a fixed format. -In case of bigquery external table, format should be -`projectname.datasetname.tablename`. -After the name is provided, `optimus` will create a file in configured datastore -directory. Open the created specification file and add additional spec details -as follows: - -```yaml -version: 1 -name: temporary-project.optimus-playground.first_table -type: external_table -labels: - usage: testexternaltable - owner: optimus -spec: - description: "example description" - schema: - - name: colume1 - type: INTEGER - - name: colume2 - type: TIMESTAMP - description: "example field 2" - source: - type: google_sheets - uris: - - https://docs.google.com/spreadsheets/d/spreadsheet_id - config: - range: Sheet1!A1:B4 # Range of data to be ingested in format of [Sheet Name]![Cell Range] - skip_leading_rows: 1 # Row of records to skip -``` - -This will add labels, description, schema, and external table source specification depending -on the type of external table. - -Optimus generates specification on the root directory inside datastore with directory -name same as resource name, although you can change directory name to whatever you -find fit to organize resources. Directory structures inside datastore doesn't -matter as long as `resource.yaml` is in a unique directory. - -For example following is a valid directory structure - -```shell -./ -./bigquery/temporary-project/optimus-playground/resource.yaml -./bigquery/temporary-project/optimus-playground/first_external_table/resource.yaml -``` - -### Creating external table over REST - -Optimus exposes Create/Update rest APIS - -``` -Create: POST /api/v1beta1/project/{project_name}/namespace/{namespace}/datastore/{datastore_name}/resource -Update: PUT /api/v1beta1/project/{project_name}/namespace/{namespace}/datastore/{datastore_name}/resource -Read: GET /api/v1beta1/project/{project_name}/namespace/{namespace}/datastore/{datastore_name}/resource/{resource_name} -``` - -```json -{ - "resource": { - "version": 1, - "name": "temporary-project.optimus-playground.first_table", - "datastore": "bigquery", - "type": "external_table", - "labels": { - "usage": "testexternaltable", - "owner": "optimus" - }, - "spec": { - "description": "example description", - "schema": [ - { - "name": "column1", - "type": "INTEGER" - }, - { - "name": "column2", - "type": "TIMESTAMP", - "description": "example description", - "mode": "required" - } - ], - "source": { - "type": "google_sheets", - "uris": ["https://docs.google.com/spreadsheets/d/spreadsheet_id"], - "config": { - "range": "Sheet1!A1:B4", - "skip_leading_rows": 1 - } - } - } - } -} -``` - -### Creating external table over GRPC - -Optimus in RuntimeService exposes an RPC - -```protobuf -rpc CreateResource(CreateResourceRequest) returns (CreateResourceResponse) {} - -message CreateResourceRequest { - string project_name = 1; - string datastore_name = 2; - ResourceSpecification resource = 3; - string namespace = 4; -} -``` - -Function payload should be self-explanatory other than the struct `spec` part which -is very similar to how json representation look. - -Spec will use `structpb` struct created with `map[string]interface{}` -For example: - -```go -map[string]interface{ - "description": "example description", - "schema": []interface{ - map[string]interface{ - "name": "colume1", - "type": "integer" - }, - map[string]interface{ - "name": "colume2", - "type": "timestamp" - "description": "some description", - "mode": "required" - }, - }, - "source": map[string]interface{ - "type": "google_sheets", - "uris": []string{"https://docs.google.com/spreadsheets/d/spreadsheet_id"}, - "config": map[string]interface{ - "range": "Sheet1!A1:B4", - "skip_leading_rows": 1 - } - }, -} -``` diff --git a/docs/docs/guides/create-bigquery-table.md b/docs/docs/guides/create-bigquery-table.md deleted file mode 100644 index 4487f8d37c..0000000000 --- a/docs/docs/guides/create-bigquery-table.md +++ /dev/null @@ -1,178 +0,0 @@ ---- -id: create-bigquery-table -title: Create bigquery table ---- - -A BigQuery table contains individual records organized in rows. Each record is -composed of columns (also called fields). -Every table is defined by a schema that describes the column names, data types, -and other information. You can specify the schema of a table when it is created. -At the moment only native table is supported. - -There are 3 ways to create a table: - -### Creating table with Optimus - -Supported datastore can be selected by calling -```bash -optimus resource create -``` -Optimus will request a resource name which should be unique across whole datastore. -All resource specification contains a name field which conforms to a fixed format. -In case of bigquery table, format should be -`projectname.datasetname.tablename`. -After the name is provided, `optimus` will create a file in configured datastore -directory. Open the created specification file and add additional spec details -as follows: -```yaml -version: 1 - -# unique name that must conform to validations of type of resource we are creating -# in case of bigquery table, this is fully qualified name -name: temporary-project.optimus-playground.first_table - -# type of resource that belong to this datastore -# e.g.: table, dataset, view -type: table - -# labels being passed to datastore which will be injected in the bigquery table -labels: - usage: testtable - owner: optimus - -# actual specification details that matches the type we are trying to create/update -spec: - description: "example description" - schema: - - name: colume1 # name of the column - type: INTEGER # datatype of the column - - name: colume2 - type: TIMESTAMP - description: "example field 2" # description for the column - mode: required # possible options (repeated/required/nullable), default is nullable - - name: colume3 - type: STRUCT - schema: # nested struct schema - - name: colume_a_1 - type: STRING - cluster: - using: [colume1] - partition: # leave empty as {} to partition by ingestion time - field: colume2 # column name - type: day # day/hour, default: day -# expiration: 24 # in hours -# range: -# start: 30 -# end: 60 -# interval: 2 -# expiration_time: 200 # in hours - -``` -This will add labels, description, schema, clustering, partition over colume2 by day -on the table once the `deploy` command is invoked. - -Optimus generates specification on the root directory inside datastore with directory -name same as resource name, although you can change directory name to whatever you -find fit to organize resources. Directory structures inside datastore doesn't -matter as long as `resource.yaml` is in a unique directory. - -For example following is a valid directory structure -```shell -./ -./bigquery/temporary-project/optimus-playground/resource.yaml -./bigquery/temporary-project/optimus-playground/first_table/resource.yaml -``` - -### Creating table over REST - -Optimus exposes Create/Update rest APIS -``` -Create: POST /api/v1beta1/project/{project_name}/namespace/{namespace}/datastore/{datastore_name}/resource -Update: PUT /api/v1beta1/project/{project_name}/namespace/{namespace}/datastore/{datastore_name}/resource -Read: GET /api/v1beta1/project/{project_name}/namespace/{namespace}/datastore/{datastore_name}/resource/{resource_name} -``` - -```json -{ - "resource": { - "version": 1, - "name": "temporary-project.optimus-playground.first_table", - "datastore": "bigquery", - "type": "table", - "labels": { - "usage": "testdataset", - "owner": "optimus" - }, - "spec": { - "description": "example description", - "schema": [ - { - "name": "column1", - "type": "INTEGER" - }, - { - "name": "column2", - "type": "TIMESTAMP", - "description": "example description", - "mode": "required" - }, - { - "name": "column3", - "type": "STRUCT", - "schema": [ - { - "name": "column_a_1", - "type": "STRING" - } - ] - } - ], - "partition": { - "field": "column2" - }, - "cluster": { - "using": ["column1"] - } - } - } -} -``` - -### Creating table over GRPC - -Optimus in RuntimeService exposes an RPC -```protobuf -rpc CreateResource(CreateResourceRequest) returns (CreateResourceResponse) {} - -message CreateResourceRequest { - string project_name = 1; - string datastore_name = 2; - ResourceSpecification resource = 3; - string namespace = 4; -} -``` -Function payload should be self-explanatory other than the struct `spec` part which -is very similar to how json representation look. - -Spec will use `structpb` struct created with `map[string]interface{}` -For example: -```go -map[string]interface{ - "description": "example description", - "schema": []interface{ - map[string]interface{ - "name": "colume1", - "type": "integer" - }, - map[string]interface{ - "name": "colume2", - "type": "timestamp" - "description": "some description", - "mode": "required" - }, - }, - "partition": map[string]interface{ - "field": "column2" - }, -} -``` diff --git a/docs/docs/guides/create-bigquery-view.md b/docs/docs/guides/create-bigquery-view.md deleted file mode 100644 index fff4a890e7..0000000000 --- a/docs/docs/guides/create-bigquery-view.md +++ /dev/null @@ -1,103 +0,0 @@ ---- -id: create-bigquery-view -title: Create bigquery view ---- - -A view is a virtual table defined by a SQL query. When you create a view, -you query it in the same way you query a table. When a user queries the view, -the query results contain data only from the tables and fields specified in the -query that defines the view. -At the moment only standard view is supported. - -There are 3 ways to create a view: - -### Creating table with Optimus - -Supported datastore can be selected by calling -```bash -optimus resource create -``` -Optimus will request a resource name which should be unique across whole datastore. -All resource specification contains a name field which conforms to a fixed format. -In case of bigquery view, format should be -`projectname.datasetname.viewname`. -After the name is provided, `optimus` will create a file in configured datastore -directory. Open the created specification file and add additional spec details -as follows: -```yaml -version: 1 -name: temporary-project.optimus-playground.first_view -type: view -labels: - usage: testview - owner: optimus -spec: - description: "example description" - view_query: | - Select * from temporary-project.optimus-playground.first_table -``` -This will add labels, description, along with the query for view once the -`deploy` command is invoked. -To use text editor intellisense for SQL formatting and linting, view query can -also be added in a separate file inside the same directory with the name `view.sql`. -Directory will look something like: -```shell -./ -./bigquery/temporary-project.optimus-playground.first_view/resource.yaml -./bigquery/temporary-project.optimus-playground.first_view/view.sql -``` -Remove the `view_query` field from the resource specification if the query is -specified in a seperate file. - -### Creating table over REST - -Optimus exposes Create/Update rest APIS -``` -Create: POST /api/v1beta1/project/{project_name}/namespace/{namespace}/datastore/{datastore_name}/resource -Update: PUT /api/v1beta1/project/{project_name}/namespace/{namespace}/datastore/{datastore_name}/resource -Read: GET /api/v1beta1/project/{project_name}/namespace/{namespace}/datastore/{datastore_name}/resource/{resource_name} -``` - -```json -{ - "resource": { - "version": 1, - "name": "temporary-project.optimus-playground.first_view", - "datastore": "bigquery", - "type": "view", - "labels": { - "usage": "testview", - "owner": "optimus" - }, - "spec": { - "description": "example description", - "view_query": "Select * from temporary-project.optimus-playground.first_table" - } - } -} -``` - -### Creating table over GRPC - -Optimus in RuntimeService exposes an RPC -```protobuf -rpc CreateResource(CreateResourceRequest) returns (CreateResourceResponse) {} - -message CreateResourceRequest { - string project_name = 1; - string datastore_name = 2; - ResourceSpecification resource = 3; - string namespace = 4; -} -``` -Function payload should be self-explanatory other than the struct `spec` part which -is very similar to how json representation look. - -Spec will use `structpb` struct created with `map[string]interface{}` -For example: -```go -map[string]interface{ - "description": "example description", - "view_query": "Select * from temporary-project.optimus-playground.first_table" -} -``` \ No newline at end of file diff --git a/docs/docs/guides/create-job.md b/docs/docs/guides/create-job.md deleted file mode 100644 index 883dc2c1d9..0000000000 --- a/docs/docs/guides/create-job.md +++ /dev/null @@ -1,122 +0,0 @@ ---- -id: create-job -title: Using Optimus to create a Job ---- - -A Job is the fundamental execution unit of an Optimus data pipeline. -It can be scheduled, configured and is always mapped to a single transformation type -(eg, BQ-to-BQ, GCS-to-BQ, etc). It can have dependencies over other jobs and should -only execute once the dependent job is successfully completed. - -A job can also be configured with Hooks as part of its lifecycle, which can be -triggered before or after the job. Please go through [concepts](../concepts/overview.md) -to know more about it. - -Let's start with a basic example. For our tutorial, we'll be creating a job that -writes "hello YYYY-MM-DD" to a table every day at 3 am. We'll use BQ-to-BQ transformation type. -For the purpose of this tutorial, we'll assume that the Google Cloud Project name -is "example" & dataset is just called "data". - - -## Creating a Job - -Open your terminal and create a new directory that will hold the specifications -created by `optimus` (The CLI of Optimus). Once ready, you can run the following -command and answer the corresponding prompts (do note that some prompts -would be to select from options instead of input): - -``` -$ optimus job create -? What is the job name? example_job -? Who is the owner of this job? example@example.com -? Which task to run? bq2bq -? Specify the start date 2021-02-18 -? Specify the interval (in crontab notation) 0 3 * * * -? Project ID: example -? Dataset Name: data -? Table Name: hello_table -? Load method to use on destination? APPEND -``` - -Note: The cron schedule of a Job is as per UTC timezone. -With the above prompt, we have created a Job with name example_job that writes to table `hello_table` -every day at 3 AM UTC, with the load method APPEND (we'll come back to this later). -The task `bq2bq` refers to "BigQuery to BigQuery" transformation. As you can notice, -each Job is mapped with a specific table. This will create the following files: - -``` -. -└── example_job - ├── assets - │ └── query.sql - └── job.yaml -``` - -You can now edit `query.sql` and write the SQL query in it. for example: - -```bash -$ cat > example_job/assets/query.sql <<_EOF -select CONCAT("Hello, ", "{{.DEND}}") as message -_EOF -``` - -`{{.DEND}}` is a macro that is replaced with the current execution date (in YYYY-MM-DD format) -of the task (note that this is the execution date of when the task was supposed to run, -not when it actually runs). There's another corresponding macro for start date called ` -{{.DSTART}}` the value of which is DEND minus the task window. If the task window is DAILY, -DSTART is one day behind DEND, if the window is weekly, DSTART is 7 days before DEND. -Do note the format of macros, these are as per [golang template](https://golang.org/pkg/text/template/). - -What about the load method then? Load method specifies write disposition of the task. -There are currently 3 configurations available: -- APPEND -- REPLACE -- MERGE - -When the load method is set to APPEND new rows are inserted to the table/partition -when the job runs, REPLACE will truncate the table/partition before writing new rows -and MERGE is used when you want to use BigQuery DML/Scripts. Which load method you use depends -on the nature of the transformation, however it's advised to use the REPLACE method -with a partitioned table to keep your queries idempotent. Another alternative would -be to use the MERGE load method with DML. Keeping queries idempotent helps backfilling data. - -Finally, this is how our Job Specification will look like (example_job/job.yaml): -```yaml -version: 1 -name: example_job -owner: example@example.com -schedule: - start_date: "2021-02-18" - interval: 0 3 * * * -behavior: - depends_on_past: false - catch_up: true -task: - name: bq2bq - config: - PROJECT: example - DATASET: data - TABLE: hello_table - LOAD_METHOD: APPEND - SQL_TYPE: STANDARD - window: - size: 24h - offset: "0" - truncate_to: d -labels: - orchestrator: optimus -dependencies: [] -hooks: [] -``` - -Now you can finally push all the files in a git repository. Create a commit and -push to repository which will initiate gitlab pipeline and apply all of your changes. -In this case: - -1. Table is migrated in BigQuery for above bq2bq task [TODO] -2. Compiles your job specifications as Airflow DAG definitions and upload them to - Google cloud storage (or any other configured store) that gets synced to airflow - (or any other configured scheduler) linked with this git repository. - -Optimus also supports managing Job Specifications via APIs. We'll talk about this in other sections. -You have now successfully deployed your transformation job onto your infrastructure. \ No newline at end of file diff --git a/docs/docs/guides/extension.md b/docs/docs/guides/extension.md deleted file mode 100644 index 824c7f0433..0000000000 --- a/docs/docs/guides/extension.md +++ /dev/null @@ -1,214 +0,0 @@ ---- -id: extension -title: Work with Extension ---- - -Extension helps the user to include third-party or arbitrary implementation -as part of Optimus. Currently, extension is designed for when the user -is running it as CLI. - -## Warning - -Extension is basically an executable file outside Optimus. **We do not guarantee whether an extension is safe or not**. We suggest to check the extension itself, whether it is safe to run in your local or not, before installing and running it. - -## Limitation - -Extension is designed to be similar to [Github extension](https://cli.github.com/manual/gh_extension). -However, since it's still in early stage, some limitations are there. - -* extension is only an executable file -* installation only looks at the Github asset according to the running system OS and Architecture -* convention for extension: - * extension repository should follow `optimus-extension-[name of extension]` (example: [optimus-extension-valor](https://github.com/gojek/optimus-extension-valor)) - * asset being consdered is binary with suffix `...[OS]-[ARC]` (example: when installing [`valor`](https://github.com/gojek/optimus-extension-valor), if the user's OS is Linux and the architecture is AMD64, then installation will consider `valor_linux-amd64` as binary to be executed) - -## Creating - -Extension is designed to be open. Anyone could create their own extension. And as long as it is avilable, anyone could install it. In order to create it, the following is the basic steps to do: - -1. Decide the name of the extension, example: `valor` -2. Create a Github repository that follows the convention, example: `optimus-extension-valor` -3. Put some implementation and asset with name based on the convention, example: `valor_linux-amd64`, `valor_darwin-amd64`, and more. -4. Ensure it is available for anyone to download - -## Commands - -Optimus support some commands to help operating on extension. - -### Installation - -The user can run installation using Optimus sub-command `install` under `extension`. -In order to install extension, run the following command: - -```zsh -optimus extension install REMOTE [flags] -``` - -The user can use `--alias` flag to change the command name, since by default, Optimus -will try to figure it out by itself. Although, during this process, sometime -an extension name conflict with the reserved commands. This flag helps to resolve that. -But, do note that this flag cannot be used to rename an **installed** extension. -To do such a thing, check [rename](#rename). - -REMOTE is the Github remote path where to look for the extension. -REMOTE can be in the form of: - -* OWNER/PROJECT -* github.com/OWNER/PROJECT -* https://www.github.com/OWNER/PROJECT - -One example of such extension is [`Valor`](https://github.com/gojek/optimus-extension-valor). -So, going back to the example above, installing it is like this: - -```zsh -optimus extension install gojek/optimus-extension-valor -``` - -or - -```zsh -optimus extension install github.com/gojek/optimus-extension-valor -``` - -or - -```zsh -optimus extension install https://github.com/gojek/optimus-extension-valor -``` - -Installation process is then in progress. If installation is a success, the user can show it by running: - -```zsh -optimus --help -``` - -A new command named after the extension will be available. -For example, if the extension name is `optimus-extension-valor`, then by default the command named `valor` will be available. -If the user wish to change it, they can use `--alias` during installation, or -`rename` it (explained later). - -The following is example when running `optimus` (without any command): - -```zsh -... -Available Commands: - ... - extension Operate with extension - ... - valor Execute gojek/optimus-extension-valor [v0.0.2] extension - version Print the client version information -... -``` - -### Executing - -In order to execute an extension, make sure to follow the installation process described [above](#installation). -After installation is finished, simply run the extension with the following command: - -```zsh -optimus [extension name or alias] -``` - -Example of `valor`: - -```zsh -optimus valor -``` - -### Operation - -The user can do some operations to an extension. This section explain more about the available commands. Do note that these commands are available on the installed extensions. -For more detail, run the following command: - -```zsh -optimus extension [extension name or alias] -``` - -Example: - -```zsh -optimus extension valor -``` - -The above command shows all available commands for `valor` extension. - -Output: - -```zsh -Sub-command to operate over extension [gojek/optimus-extension-valor@v0.0.4] - -USAGE - optimus extension valor [flags] - -CORE COMMANDS - activate activate is a sub command to allow user to activate an installed tag - describe describe is a sub command to allow user to describe extension - rename rename is a sub command to allow user to rename an extension command - uninstall uninstall is a sub command to allow user to uninstall a specified tag of an extension - upgrade upgrade is a sub command to allow user to upgrade an extension command - -INHERITED FLAGS - --help Show help for command - --no-color Disable colored output - -v, --verbose if true, then more message will be provided if error encountered -``` - -#### Activate - -Activate a specific tag when running extension. For example, if the user has two version of `valor`, which is `v0.0.1` and `v0.0.2`, then by specifying the correct tag, the user can just switch between tag. - -Example: - -```zsh -optimus extension valor activate v0.0.1 -``` - -#### Describe - -Describes general information about an extension, such information includes all -available releases of an extension in the local, which release is active, -and more. - -Example: - -```zsh -optimus extension valor describe -``` - -#### Rename - -Rename a specific extension to another command that are not reserved. -By default, Optimus tries to figure out the appropriate command name from its project name. -However, sometime the extension name is not convenient like it being too long or the user -just want to change it. - -Example: - -```zsh -optimus extension valor rename vl -``` - -#### Uninstall - -Uninstalls extension as a whole or only a specific tag. This allows the user to do -some clean up to preserve some storage or to resolve some issues. -By default, Optimus will uninstall the extension as a whole. To target a specific tag, -use flag `--tag`. - -Example: - -```zsh -optimus extension valor uninstall -``` - -#### Upgrade - -Upgrade allows the user to upgrade a certain extension to its latest tag. -Although the user can use the install command, but using this command is shorter -and easier as the user only needs to specify the installed extension. - -Example: - -```zsh -optimus extension valor upgrade -``` diff --git a/docs/docs/guides/manage-secrets.md b/docs/docs/guides/manage-secrets.md deleted file mode 100644 index 1915412e0a..0000000000 --- a/docs/docs/guides/manage-secrets.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -id: secret -title: Manage Secrets ---- - -During job execution, specific credentials are needed to access required resources, for example, BigQuery credential -for BQ to BQ tasks. Users are able to register secrets on their own, manage it, and use it in tasks and hooks. -Please go through [concepts](../concepts/overview.md) to know more about it. - -## Registering secret with Optimus - -Register a secret by running the following command: -```shell -$ optimus secret set someSecret someSecretValue -```` - -By default, Optimus will encode the secret value. However, to register secret that has been encoded, run the following -command instead: -```shell -$ optimus secret set someSecret encodedSecretValue --base64 -```` - -There is also a flexibility to register using an existing secret file, instead of providing the secret value in the -command. -```shell -$ optimus secret set someSecret --file=/path/to/secret -``` - -Please note that registering a secret which already exists will result in error. Modifying an existing secret can be -done using the Update command. - -## Updating a secret - -The update-only flag is generally used when you explicitly only want to update a secret which already exists -and don't want to create it by mistake. - -```shell -$ optimus secret set someSecret someSecretValue --update-only -``` - -It will return an error if the secret to update does not exist already. - - -## Listing a secret - -The list command can be used to show the user defined secrets which are registered with optimus. It will list -the namespace associated for a secret. - -```shell -$ optimus secret list -Secrets for project: optimus-local - NAME | DIGEST | NAMESPACE | DATE --------------+----------------------------------------------+-----------+----------------------- - secret1 | SIBzsgUuHnExBY4qSzqcrlrb+3zCAHGu/4Fv1O8eMI8= | * | 2022-04-12T04:30:45Z -``` - -It shows a digest for the encrypted secret, so as not to send the cleartext password on the network. diff --git a/docs/docs/guides/optimus-serve.md b/docs/docs/guides/optimus-serve.md deleted file mode 100644 index 4a769eb1e3..0000000000 --- a/docs/docs/guides/optimus-serve.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -id: optimus-serve -title: Starting Optimus Server ---- - -Once the optimus binary is installed, it can be started in serve mode using -```shell -optimus serve -``` -It needs few [configurations](../getting-started/configuration.md) as prerequisites, for example, create a `config.yaml` file with -```yaml -version: 1 -host: localhost:9100 -serve: - port: 9100 - host: localhost - ingress_host: optimus.example.io:80 - app_key: 32charrandomhash32charrandomhash - db: - dsn: postgres://user:password@localhost:5432/optimus?sslmode=disable -``` -You will need to change `dsn` and `app_key` according to your installation. And then run `optimus serve -c config.yaml` - -Once the server is up and running, before it is ready to deploy `jobs` we need to -- Register an optimus project -- Register a namespace under project -- Register required secrets under project - -This needs to be done in order using REST/GRPC endpoints provided by the server. \ No newline at end of file diff --git a/docs/docs/guides/organising-specifcations.md b/docs/docs/guides/organising-specifcations.md deleted file mode 100644 index 60dc9e862a..0000000000 --- a/docs/docs/guides/organising-specifcations.md +++ /dev/null @@ -1,133 +0,0 @@ ---- -id: organising-specifications -title: Organising specifications ---- - -Optimus supports two ways to deploy specifications -- REST/GRPC -- Optimus CLI deploy command - -When using Optimus cli to deploy, either manually or from a CI pipeline, it is advised to use version control system like git. Here is a simple directory structure that can be used as a template for jobs and datastore resources. - - -``` -. -├── optimus.yaml -├── README.md -├── namespace-1 -│   ├── jobs -| │   ├── job1 -| │   ├── job2 -| │   └── this.yaml -│   └── resources -|    ├── bigquery -│      │ ├── table1 -│      │   ├── table2 -|    | └── this.yaml -│   └── postgres -│   └── table1 -└── namespace-2 -    └── jobs -    └── resources -``` - -A sample `optimus.yaml` would look like - -```yaml -version: 1 -host: localhost:9100 -project: - name: project-1 -namespaces: -- name: namespace-1 - config: {} - job: - path: namespace-1/jobs - datastore: - - type: bigquery - path: namespace-1/resources/bigquery - - type: postgres - path: namespace-1/resources/postgres -- name: namespace-2 - config: {} - job: - path: namespaces-2/jobs - datastore: - - type: bigquery - path: namespace-2/resources/bigquery -``` - - - -You might have also noticed there are `this.yaml` files being used in some directories. This file is used to share a single set of configuration across multiple sub directories. For example if I create a file at `/namespace-1/jobs/this.yaml`, then all sub directories inside `/namespaces-1/jobs` will inherit this config as defaults. If same config is specified in sub directory, then sub directory will override the parent defaults. For example a `this.yaml` in `/namespace-1/jobs` - -```yaml -version: 1 -schedule: - interval: @daily -behavior: - depends_on_past: false - catch_up: true - retry: - count: 1 - delay: 5s -labels: - owner: overlords - transform: sql -``` - -and a `job.yaml` in `/namespace-1/jobs/job1` - -```yaml -name: sample_replace -owner: optimus@example.io -schedule: - start_date: "2020-09-25" - interval: 0 10 * * * -behavior: - depends_on_past: true -task: - name: bq2bq - config: - project: project_name - dataset: project_dataset - table: sample_replace - load_method: REPLACE - window: - size: 48h - offset: 24h -labels: - process: bq -``` - -will result in final computed `job.yaml` during deployment as - -```yaml -version: 1 -name: sample_replace -owner: optimus@example.io -schedule: - start_date: "2020-10-06" - interval: 0 10 * * * -behavior: - depends_on_past: true - catch_up: true - retry: - count: 1 - delay: 5s -task: - name: bq2bq - config: - project: project_name - dataset: project_dataset - table: sample_replace - load_method: REPLACE - window: - size: 48h - offset: 24h -labels: - process: bq - owner: overlords - transform: sql -``` - diff --git a/docs/docs/guides/predator.md b/docs/docs/guides/predator.md deleted file mode 100644 index f124f24d1e..0000000000 --- a/docs/docs/guides/predator.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -id: profiling-auditing -title: Profiling and Auditing Bigquery ---- - -# Profiling and Auditing BigQuery - -To enable Profiler and Auditor (Predator), answer the related questions in Job specification. -Note: this is not available for public use at the moment - -```bash -• $ optimus job addhook -? What is the job name? test_job -? Who is the Owner of this job? de@go-jek.com -? Specify the start date (YYYY-MM-DD) 2021-01-01 -? Specify the interval (in crontab notation) @daily -? Enable profile for the destination table? true -? Enable audit for the destination table? true -? Filter expression for profiling? (empty for always do full scan profiling) -event_timestamp >= '{{.DSTART}}' AND event_timestamp < '{{.DEND}}' -? Specify the profile/audit result grouping field (empty to not group the result) __PARTITION__ -? Choose the profiling mode complete -``` - -Configs: -- **Filter expression**: Expression is used as a where clause to restrict the number of rows to only profile the ones - that needed to be profiled. - Expression can be templated with: DSTART and DEND. These will be replaced with the window for which the current - transformation is getting executed. EXECUTION_TIME will be replaced with job execution time that is being - used by the transformation task. `__PARTITION__` represents the partitioning field of the table and the type of - partition. If it is a daily partition using field `event_timestamp`, then the macros is equal to date - `event_timestamp`. -- **Group**: Represent the column on which the records will be grouped for profiling. Can be `__PARTITION__` or any other - field in the target table. -- **Mode**: Mode represents the profiling strategy used with the above configurations, it doesn’t affect the profile - results. `complete` means all the records in a given group are considered for profiling. ‘incremental’ only the newly added records for the given group are considered for profiling. This input is needed when DataQuality results are shown in UI. - -Here is a sample DAG specification that has Predator enabled. -```yaml -version: 1 -name: test_job -owner: de@go-jek.com -schedule: - start_date: "2021-02-26" - interval: 0 2 * * * -behavior: - depends_on_past: false - catch_up: true -task: - name: bq2bq - config: - DATASET: playground - LOAD_METHOD: REPLACE - PROJECT: gcp-project - SQL_TYPE: STANDARD - TABLE: hello_test_table - window: - size: 24h - offset: "0" - truncate_to: d -dependencies: [] -hooks: - - name: predator - config: - AUDIT_TIME: '{{.EXECUTION_TIME}}' - BQ_DATASET: '{{.TASK__DATASET}}' - BQ_PROJECT: '{{.TASK__PROJECT}}' - BQ_TABLE: '{{.TASK__TABLE}}' - FILTER: 'event_timestamp >= "{{.DSTART}}" AND event_timestamp < "{{.DEND}}"' - GROUP: __PARTITION__ - MODE: complete - PREDATOR_URL: '{{.GLOBAL__PREDATOR_HOST}}' - SUB_COMMAND: profile_audit -``` - -After the Job is created, create a Data Quality Spec of the particular table and -place it in the Optimus jobs repository, inside the Predator directory. -Detail of quality spec creation is available in Predator documentation. diff --git a/docs/docs/guides/publishing-from-bigquery-to-kafka.md b/docs/docs/guides/publishing-from-bigquery-to-kafka.md deleted file mode 100644 index 4bdd4ed868..0000000000 --- a/docs/docs/guides/publishing-from-bigquery-to-kafka.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -id: publishing-bigquery-to-kafka -title: Publishing from bigQuery to kafka ---- - -Following are the steps to publish BQ data to Kafka: - -1. Raise a ticket with DE to create the proto, soon this will be automated. -2. As part of Job Specification answer the questions related to pushing data to Kafka. -3. Commit and Push - -Publishing from Bigquery to Kafka is done using an Optimus hook called `Transporter`. -Once data is pushed to Kafka you can use firehose to consume messages for your needs. -In order to add hook to an existing Job, run the following command and answer the -corresponding prompts: - -``` -$ ./optimus job addhook -? Select a Job example_job -? Which hook to run? transporter -? Filter expression for extracting transformation rows? event_timestamp >= '{{.DSTART}}' AND event_timestamp < '{{.DEND}}' -``` -Note: this is not available for public use at the moment - -### Config: - -- Filter expression: Expression is used as a where clause to restrict the number of rows to only push the ones -that are affected by current transformation. Expression can be templated with DSTART and DEND macros which will be replaced with the window for which the current transformation is getting executed similar to what we do in Job specifications. - -After this, existing job.yaml file will get updated with the new hooks config and the job specification would look like below: - -```yaml -version: 1 -name: example_job -owner: example@example.com -schedule: - start_date: "2021-02-18" - interval: 0 3 * * * -behavior: - depends_on_past: false - catch_up: true -task: - name: bq2bq - config: - DATASET: data - LOAD_METHOD: APPEND - PROJECT: example - SQL_TYPE: STANDARD - TABLE: hello_table - window: - size: 24h - offset: "0" - truncate_to: d -dependencies: [] -hooks: -- name: transporter - config: - BQ_DATASET: '{{.TASK__DATASET}}' # inherited from task configs - BQ_PROJECT: '{{.TASK__PROJECT}}' - BQ_TABLE: '{{.TASK__TABLE}}' - FILTER_EXPRESSION: 'event_timestamp >= "{{.DSTART}}" AND event_timestamp < "{{.DEND}}"' - KAFKA_TOPIC: optimus_example-data-hello_table - PRODUCER_CONFIG_BOOTSTRAP_SERVERS: '{{.GLOBAL__TRANSPORTER_KAFKA_BROKERS}}' - PROTO_SCHEMA: example.data.HelloTable - STENCIL_URL: '{{.GLOBAL__TRANSPORTER_KAFKA_BROKERS}}' # will be defined as global config -``` - -Some configuration would be auto-generated by Optimus. -You can now commit and push the changes in Job specification. - -There are standards that we want users to maintain but haven’t enforced: - -1. Single table to topic mapping to be maintained & following a naming convention - helps in better discoverability. Topic names are auto populated with the same naming convention. -2. Kafka Brokers are not modifiable & pre-selected per every entity. -3. All Protos & Stencil are managed & doesn’t need any user intervention. - -### Bigquery and Protobuf Data Type Mapping - -| Bigquery Type | Protobuf Type | -|---------------|---------------------------| -| STRING | string | -| BYTES | bytes | -| INTEGER | int64 | -| FLOAT | float | -| BOOLEAN | bool | -| TIMESTAMP | google.protobuf.Timestamp | -| DATE | string | -| TIME | string | -| DATETIME | string | -| NUMERIC | float | -| GEOGRAPHY | string | diff --git a/docs/docs/guides/refresh-jobs.md b/docs/docs/guides/refresh-jobs.md deleted file mode 100644 index 5604a2d422..0000000000 --- a/docs/docs/guides/refresh-jobs.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -id: refresh-jobs -title: Refresh Jobs ---- - -Jobs might need to be refreshed on certain cases, for example: -* When job source is a `view` and the source view has been modified. -* When there is some issues on job dependencies or priorities and need to do a clean up -* When jobs need to have its plugin refreshed to the latest version - -Optimus refresh will try to resolve the dependencies for the **requested** jobs and deploy **all** jobs in the project. - -## Refresh jobs - -Refresh all jobs in the requested project: -```shell -$ optimus job refresh --project sample-project --verbose -``` -Note: use verbose flag to show list of jobs being refreshed and deployed - -### Refresh jobs on selected namespaces -Use `namespaces` flag to refresh only the selected namespace. Only all the jobs in the selected namespaces will be -refreshed, but all the jobs in the project will be deployed. -```shell -$ optimus job refresh --project sample-project --namespaces namespace-a,namespace-b -``` - -### Refresh only selected jobs -Use `jobs` flag to only refresh selected jobs. Only the selected jobs will be refreshed, but all the jobs in the project -will be deployed. -```shell -$ optimus job refresh --project sample-project --namespaces namespace-a --jobs job-a,job-b -``` -Note: All the selected jobs should be inside the same namespace. \ No newline at end of file diff --git a/docs/docs/guides/replay.md b/docs/docs/guides/replay.md deleted file mode 100644 index f57ad8044e..0000000000 --- a/docs/docs/guides/replay.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -id: replay -title: Backfill jobs using Replay ---- - -Some old dates of a job might need to be re-run (backfill) due to business requirement changes, corrupt data, or other -various reasons. Optimus provides an easy way to do this using Replay. Please go through -[concepts](../concepts/overview.md) to know more about it. - -## Run a replay - -In order to run a replay, run the following command: - -```shell -$ optimus replay create sample-job 2021-01-01 2021-02-01 --project sample-project --namespace sample-namespace -``` - -Replay accepts three arguments, first is the DAG name that is used in optimus specification, second is -start date of replay, third is end date (optional) of replay. - -If the replay request passes the basic validation, you will see all the tasks including the downstream that will be -replayed. You can confirm to proceed to run replay if the run simulation is as expected. - -Once your request has been successfully replayed, this means that Replay has cleared the mentioned task in the scheduler. -Please wait until the scheduler finishes scheduling and running those tasks. - -## Get a replay status - -You can check the replay status using the replay ID given previously and use in this command: - -```shell -$ optimus replay status {replay_id} --project sample-project -``` - -You will see the latest replay status including the status of each run of your replay. - -## Get list of replays - -List of recent replay of a project can be checked using this sub command: - -```shell -$ optimus replay list --project sample-project -``` - -Recent replay ID including the job, time window, replay time, and status will be shown. To check the detailed status of a -replay, please use the `status` sub command. - -## Run a replay dry run - -A dry run is also available to simulate all the impacted tasks without actually re-running the tasks. Example of dry run -usage: - -```shell -$ optimus replay create sample-job 2021-01-01 2021-02-01 --project sample-project --namespace sample-namespace --dry-run -``` diff --git a/docs/docs/guides/task-bq2bq.md b/docs/docs/guides/task-bq2bq.md deleted file mode 100644 index 22a0e3ab2e..0000000000 --- a/docs/docs/guides/task-bq2bq.md +++ /dev/null @@ -1,171 +0,0 @@ ---- -id: task-bq2bq -title: Bigquery to bigquery transformation task ---- - -### Creating Task -Command to create a task : -``` -optimus job create -``` -This command will invoke an interactive cli that contains configurations that -need to be filled for the task. The tasks files will be generated at -`{PWD}/jobs/{JOB_NAME}/assets` folder. - -Inside the assets folder there could be several files, one that is -needed to configure this task is : - -* query.sql - file that contains the transformation query - -This will also configure the `job.yaml` with few defaults and few inputs requested at the time -of creation. User still able to change the config values after the file is generated. - -For example `job.yaml` config : - -```yaml -version: 1 -name: example_job -owner: example@example.com -schedule: - start_date: "2021-02-18" - interval: 0 3 * * * -behavior: - depends_on_past: false - catch_up: true -task: - name: bq2bq - config: - DATASET: data - LOAD_METHOD: APPEND - PROJECT: example - SQL_TYPE: STANDARD - TABLE: hello_table - TASK_BQ2BQ: "{{.secret.TASK_BQ2BQ}}" - GOOGLE_APPLICATION_CREDENTIALS: "/tmp/auth.json" - window: - size: 24h - offset: "0" - truncate_to: d -``` - -Here are the details of each configuration and the allowed values : - -| Config Name | Description | Values | -| ----------------------- |-----------------------------------------------------------------------------------------------------------------| ------------------------------------| -| `PROJECT` | google cloud platform project id of the destination bigquery table | ... | -| `DATASET` | bigquery dataset name of the destination table | ... | -| `TABLE` | the table name of the destination table | ... | -| `LOAD_METHOD` | method to load data to the destination tables | APPEND, REPLACE, MERGE | -| `PARTITION_FILTER` | Used to identify target partitions to replace in a REPLACE query. This can be left empty and optimus will figure the target partitions automatically but its cheaper and faster to specify the condition. This filter will be used as a where clause in a merge statement to delete the partitions from the destination table. | event_timestamp >= "{{.DSTART}}" AND event_timestamp < "{{.DEND}}" | - -### Load Method - -The way data loaded to destination table depends on the partition configuration of the destination tables - -| Load Method | No Partition | Partitioned Table | -| -------------|------------------------------------------------------------------------------------------------| -------------------------------------------------------------------------------------------| -| APPEND | Append new records to destination table | Append new records to destination table per partition based on localised start_time | -| REPLACE | Truncate/Clean the table before insert new records | Clean records in destination partition before insert new record to new partition | -| MERGE | Load the data using DML Merge statement, all of the load logic lies on DML merge statement | Load the data using DML Merge statement, all of the load logic lies on DML merge statement | - -## query.sql file - -The *query.sql* file contains transformation logic - -```sql -select count(1) as count, date(created_time) as dt -from `project.dataset.tablename` -where date(created_time) >= '{{.DSTART|Date}}' and date(booking_creation_time) < '{{.DEND|Date}}' -group by dt -``` - -### SQL macros - -Macros is special variables in SQL that will be replaced by actual values when transformation executed - -There are several SQL macros available - -- {{.DSTART}} - start date/datetime of the window as `2021-02-10T10:00:00+00:00` - that is, RFC3339 -- {{.DEND}} - end date/datetime of the window, as RFC3339 -- {{.JOB_DESTINATION}} - full qualified table name used in DML statement -- {{.EXECUTION_TIME}} - full qualified table name used in DML statement - -The value of `DSTART` and `DEND` depends on `window` config in `job.yaml`. This is very similar to Optimus v1 - -| Window config | DSTART | DEND -| ----------------------------------- |--------------------------------------------------------------------| ---------------------------------------------------------------------| -| size:24h, offset:0, truncate_to:d | The current date taken from input, for example 2019-01-01 | The next day after DSTART date 2019-01-02 | -| size:168h, offset:0, truncate_to:w | Start of the week date for example : 2019-04-01 | End date of the week , for example : 2019-04-07 | -| size:1M, offset:0, truncate_to:M | Start of the month date, example : 2019-01-01 | End date of the month, for example : 2019-01-31 | -| size:2h, offset:0, truncate_to:h | Datetime of the start of the hour, for example 2019-01-01 01:00:00 | Datetime the start of the next hour, for example 2019-01-01 02:00:00 | - -Please find more details under [concepts](../concepts/intervals-and-windows.md) section. - -Macros in SQL transformation example : - -```sql -select count(1) as count, date(created_time) as dt -from `project.dataset.tablename` -where date(created_time) >= '{{.DSTART|Date}}' and date(booking_creation_time) < '{{.DEND|Date}}' -group by dt -``` - -Rendered SQL for DAILY window example : - -```sql -select count(1) as count, date(created_time) as dt -from `project.dataset.tablename` -where date(created_time) >= '2019-01-01' and date(booking_creation_time) < '2019-01-02' -group by dt -``` - -Rendered SQL for HOURLY window example : -the value of `DSTART` and `DEND` is YYYY-mm-dd HH:MM:SS formatted datetime - -```sql -select count(1) as count, date(created_time) as dt -from `project.dataset.tablename` -where date(created_time) >= '2019-01-01 06:00:00' and date(booking_creation_time) < '2019-01-01 07:00:00' -group by dt -``` - -destination_table macros example : - -```sql -MERGE `{{.JOB_DESTINATION}}` S -using -( -select count(1) as count, date(created_time) as dt -from `project.dataset.tablename` -where date(created_time) >= '{{.DSTART}}' and date(created_time) < '{{.DEND}}' -group by dt -) N -on S.date = N.date -WHEN MATCHED then -UPDATE SET `count` = N.count -when not matched then -INSERT (`date`, `count`) VALUES(N.date, N.count) -``` - -## SQL Helpers - -Sometimes default behaviour of how tasks are being understood by optimus is not ideal. You can change this using helpers inside the query.sql file. To use, simply add them inside sql multiline comments where it’s required. -At the moment there is only one sql helper: - -- `@ignoreupstream`: By default, Optimus adds all the external tables used inside the query file as its upstream -dependency. This helper can help ignore unwanted waits for upstream dependency to finish before the current transformation can be executed. -Helper needs to be added just before the external table name. For example: -```sql -select -hakai, -rasengan, -`over`, -load_timestamp as `event_timestamp` -from /* @ignoreupstream */ -`g-project.playground.sample_select` -WHERE -DATE(`load_timestamp`) >= DATE('{{.DSTART}}') -AND DATE(`load_timestamp`) < DATE('{{.DEND}}') -``` - diff --git a/docs/docs/introduction.md b/docs/docs/introduction.md index 4d63c6ce94..3c8cb912fd 100644 --- a/docs/docs/introduction.md +++ b/docs/docs/introduction.md @@ -5,35 +5,44 @@ title: Introduction # Optimus -Optimus is an ETL orchestration tool that helps manage warehouse resources and -schedule transformation over cron interval. Warehouses like Bigquery can be used -to create, update, read, delete different types of resources(dataset/table/standard view). -Similarly, jobs can be SQL transformations taking inputs from single/multiple -source tables executing over fixed schedule interval. Optimus was made from start -to be extensible, which is, adding support for different kind of warehouses, -transformations and executors can be done easily. - -## Features - -- BigQuery - - Schedule BigQuery transformation - - Query compile time templating (variables, loop, if statements, macros, etc) - - BigQuery Dataset/Table/View creation - - BigQuery UDF creation **[in roadmap]** - - Audit/Profile BigQuery tables - - Sink BigQuery tables to Kafka - - Automatic dependency resolution: In BigQuery if a query references - tables/views as source, jobs required to create these tables will be added - as dependencies automatically and optimus will wait for them to finish first. - - Cross tenant dependency: Optimus is a multi-tenant service, if there are two - tenants registered, serviceA and serviceB then service B can write queries - referencing serviceA as source and Optimus will handle this dependency as well - - Dry run query: Before SQL query is scheduled for transformation, during - deployment query will be dry-run to make sure it passes basic sanity - checks -- Extensibility to support Python transformation -- Git based specification management -- GRPC/REST based APIs - -NOTE: This is still in early stages and very close to use for production. -We are taking feedback and making breaking changes based on user requirements. +Optimus is an ETL orchestration tool that helps manage data transformation jobs and manage warehouse resources. +It enables you to transform your data by writing the transformation script and YAML configuration while Optimus handles +the dependency, schedules it, and handles all other aspects of running transformation jobs at scale. Optimus also supports +warehouse resource management (currently BigQuery), which enables you to create, update, and read BigQuery tables, views, and datasets. + +![High Level Optimus Diagram](/img/docs/OptimusIntro.png "OptimusIntro") + +Optimus was made to be extensible. Adding support for different kinds of sources/sinks and transformation executors +can be done easily. If your organization has to setup & manage data pipelines that are complex with multiple sources, +sinks & there are many team members managing them, then Optimus is the perfect tool for you. + +## Multi-Tenancy Support + +Optimus supports multi-tenancy. Each tenant manages their own jobs, resources, secrets, and configuration while optimus +managing dependencies across tenants. + +## Extensible + +Optimus provides the flexibility to you to define how your transformation jobs should behave, which data source or +warehouse sink you want to support, and what configurations you need from the users. This flexibility is addressed +through [plugin](concepts/plugin.md). At the moment, we provide a [BigQuery to BigQuery task plugin](https://github.com/raystack/transformers/tree/main/task/bq2bq), +but you can write custom plugins such as Python transformations. + +Also, in order to provide a unified command line experience of various tools, Optimus provides [extensions](client-guide/work-with-extension.md) +support on client side through which you can extend the capabilities for example providing governance. + +## Automated Dependency Resolution + +Optimus parses your data transformation queries and builds a dependency graph automatically without the user explicitly +defining the same. The dependencies are managed across tenants, so teams doesn’t need to coordinate among themselves. + +## In-Built Alerting + +Always get notified when your job is not behaving as expected, on failures or on SLA misses. Optimus supports +integrations with slack & pagerduty. + +## Verification in Advance + +Minimize job runtime issues by validating and inspecting jobs before submitting them which enables them for faster +turnaround time when submitting their jobs. Users can get to know about job dependencies, validation failures & some +warnings before submitting the jobs. diff --git a/docs/docs/reference/API.md b/docs/docs/reference/API.md index 743e9bb845..0ce208dc9b 100644 --- a/docs/docs/reference/API.md +++ b/docs/docs/reference/API.md @@ -1,11 +1,6 @@ ---- -id: api -title: API ---- - # API Optimus service supports REST and GRPC for interaction, currently on the same port. -- [REST API](https://github.com/odpf/optimus/blob/a32e35aef61e5d51672b1afc131e9ea828cff1a5/api/third_party/openapi/odpf/optimus/core/v1beta1/runtime.swagger.json) -- [GRPC](https://github.com/odpf/proton/blob/ef83b9e9248e064a1c366da4fe07b3068266fe59/odpf/optimus/core/v1beta1/runtime.proto) +- [REST API](https://github.com/raystack/optimus/blob/a32e35aef61e5d51672b1afc131e9ea828cff1a5/api/third_party/openapi/raystack/optimus/core/v1beta1/runtime.swagger.json) +- [GRPC](https://github.com/raystack/proton/blob/ef83b9e9248e064a1c366da4fe07b3068266fe59/raystack/optimus/core/v1beta1/runtime.proto) diff --git a/docs/docs/reference/FAQ.md b/docs/docs/reference/FAQ.md index 69e61ececc..7cb2212806 100644 --- a/docs/docs/reference/FAQ.md +++ b/docs/docs/reference/FAQ.md @@ -1,8 +1,3 @@ ---- -id: faq -title: FAQ ---- - # FAQ - **I want to run a DDL/DML query like DELETE, how can I do that?** @@ -12,4 +7,32 @@ title: FAQ - **What should not be changed once the specifications are created?** - Optimus uses Airflow for scheduling job execution and it does not support change of start_date & schedule_interval once the dag is created. For that please delete the existing one or recreate it with a different suffix. Also make sure you don’t change the dag name if you don’t want to lose the run history of Airflow. + Optimus uses Airflow for scheduling job execution and it does not support change of start_date & schedule_interval + once the dag is created. For that please delete the existing one or recreate it with a different suffix. + Also make sure you don’t change the dag name if you don’t want to lose the run history of Airflow. + +- **Can I have a job with only a transformation task (without a hook) or the other way around?** + + Transformation task is mandatory but hook is optional. You can have a transformation task without a hook but cannot + have a hook without a transformation task. + +- **I have a job with a modified view source, however, it does not detect as modified when running the job replace-all command. + How should I apply the change?** + + It does not detect as modified as the specification and the assets of the job itself is not changed. Do run the job refresh command. + +- **My job is failing due to the resource is not sufficient. Can I scale a specific job to have a bigger CPU / memory limit?** + + Yes, resource requests and limits are configurable in the job specification’s metadata field. + +- **I removed a resource specification, but why does the resource is not deleted in the BigQuery project?** + + Optimus currently does not support resource deletion to avoid any accidental deletion. + +- **I removed a job specification, but why does it still appear in Airflow UI?** + + You might want to check the log of the replace-all command you are using. It will show whether your job has been + successfully deleted or not. If not, the possible causes are the job is being used by another job as a dependency. + You can force delete it through API if needed. + + If the job has been successfully deleted, it might take time for the deletion to reflect which depends on your Airflow sync or DAG load configuration. diff --git a/docs/docs/reference/metrics.md b/docs/docs/reference/metrics.md new file mode 100644 index 0000000000..85ff959a2c --- /dev/null +++ b/docs/docs/reference/metrics.md @@ -0,0 +1,50 @@ +# Metrics + +## Job Change Metrics + +| Name | Type | Description | Labels | +|----------------------------------|---------|------------------------------------------------------|--------------------------------------------------| +| job_events_total | counter | Number of job changes attempt. | project, namespace, status | +| job_upload_total | counter | Number of jobs uploaded to scheduler. | project, namespace, status | +| job_removal_total | counter | Number of jobs removed from scheduler. | project, namespace, status | +| job_namespace_migrations_total | counter | Number of jobs migrated from a namespace to another. | project, namespace_source, namespace_destination | +| job_replace_all_duration_seconds | counter | Duration of job 'replace-all' process in seconds. | project, namespace | +| job_refresh_duration_seconds | counter | Duration of job 'refresh' process in seconds. | project | +| job_validation_duration_seconds | counter | Duration of job 'validation' process in seconds. | project, namespace | + +## JobRun Metrics + +| Name | Type | Description | Labels | +|------------------------------|---------|-----------------------------------------------------------------------------------------------------------------------|------------------------------------------| +| jobrun_events_total | counter | Number of jobrun events in a job broken by the status, e.g sla_miss, wait_upstream, in_progress, success, failed. | project, namespace, job, status | +| jobrun_sensor_events_total | counter | Number of sensor run events broken by the event_type, e.g start, retry, success, fail. | project, namespace, event_type | +| jobrun_task_events_total | counter | Number of task run events for a given operator (task name) broken by the event_type, e.g start, retry, success, fail. | project, namespace, event_type, operator | +| jobrun_hook_events_total | counter | Number of hook run events for a given operator (task name) broken by the event_type, e.g start, retry, success, fail. | project, namespace, event_type, operator | +| jobrun_replay_requests_total | counter | Number of replay requests for a single job. | project, namespace, job, status | +| jobrun_alerts_total | counter | Number of the alerts triggered broken by the alert type. | project, namespace, type | + +## Resource Metrics + +| Name | Type | Description | Labels | +|--------------------------------------|---------|---------------------------------------------------------------------------------|--------------------------------------------------| +| resource_events_total | counter | Number of resource change attempts broken down by the resource type and status. | project, namespace, datastore, type, status | +| resource_namespace_migrations_total | counter | Number of resources migrated from a namespace to another namespace. | project, namespace_source, namespace_destination | +| resource_upload_all_duration_seconds | gauge | Duration of uploading all resource specification in seconds. | project, namespace | +| resource_backup_requests_total | counter | Number of backup requests for a single resource. | project, namespace, resource, status | + +## Tenant Metrics + +| Name | Type | Description | Labels | +|----------------------|---------|------------------------------------|----------------------------| +| secret_events_total | counter | Number of secret change attempts. | project, namespace, status | + +## System Metrics + +| Name | Type | Description | Labels | +|-------------------------------------|---------|----------------------------------------------------------|--------| +| application_heartbeat | counter | Optimus server heartbeat pings. | - | +| application_uptime_seconds | gauge | Seconds since the application started. | - | +| notification_queue_total | counter | Number of items queued in the notification channel. | type | +| notification_worker_batch_total | counter | Number of worker executions in the notification channel. | type | +| notification_worker_send_err_total | counter | Number of events created and to be sent to writer. | type | +| publisher_kafka_events_queued_total | counter | Number of events queued to be published to kafka topic. | - | diff --git a/docs/docs/reference/shell-autocompletion.md b/docs/docs/reference/shell-autocompletion.md deleted file mode 100644 index e4704afb25..0000000000 --- a/docs/docs/reference/shell-autocompletion.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -id: shell-autocompletion -title: Shell autocompletion ---- -### Bash auto-completion - -The optimus completion script for Bash can be generated with `optimus completion bash`. Sourcing this script in your shell enables optimus completion. - -However, the completion script depends on bash-completion, which means that you have to install this software first (you can test if you have bash-completion already installed by running -`type _init_completion`). - - ->Warning: There are two versions of bash-completion, v1 and v2. V1 is for Bash 3.2 (which is the default on macOS), and v2 is for Bash 4.1+. The optimus completion script doesn't work correctly with bash-completion v1 and Bash 3.2. It requires bash-completion v2 and Bash 4.1+. Thus, to be able to correctly use optimus completion on macOS, you have to install and use Bash 4.1+ (instructions). The following instructions assume that you use Bash 4.1+ (that is, any Bash version of 4.1 or newer). - - -You now have to ensure that the optimus completion script gets sourced in all your shell sessions. There are multiple ways to achieve this: -- Source the completion script in your ~/.bash_profile file: - -``` -echo 'source <(optimus completion bash)' >> ~/.bash_profile -``` - -- Add the completion script to the /usr/local/etc/bash_completion.d directory: -``` -# To load completions for each session, execute once: -# Linux: -$ optimus completion bash > /etc/bash_completion.d/_optimus -# macOS: -$ optimus completion bash > /usr/local/etc/bash_completion.d/_optimus -``` - -- If you installed optimus with Homebrew (as explained in [getting started](../getting-started/installation.md)), then the optimus completion script should already be in /usr/local/etc/bash_completion.d/_optimus. In that case, you don't need to do anything. - ->Note: The Homebrew installation of bash-completion v2 sources all the files in the BASH_COMPLETION_COMPAT_DIR directory, that's why the latter two methods work. - -In any case, after reloading your shell, optimus completion should be working. - - -### Zsh Auto-completion - -The optimus completion script for Zsh can be generated with the command `optimus completion zsh`. Sourcing the completion script in your shell enables optimus autocompletion. - -- If shell completion is not already enabled in your environment, you will need to enable it. You can execute the following once: - ->If you get an error like `complete:13: command not found: compdef`, then add the following to the beginning of your `~/.zshrc` file: - -``` - $ echo "autoload -U compinit; compinit" >> ~/.zshrc -``` -- To load completions for each session, execute once: -``` - $ optimus completion zsh > "${fpath[1]}/_optimus" -``` -- Now start a new shell for this setup to take effect and execute the below command to do sourcing in all your shell session: -``` - $ source ~/.zshrc -``` - -After setup is completed -``` - # Run the following command in shell (bash/zsh) - $ optimus [tab][tab] -``` - -Output : - ``` -$ optimus -Optimus is an easy-to-use, reliable, and performant workflow orchestrator for -data transformation, data modeling, pipelines, and data quality management. - -For passing authentication header, set one of the following environment -variables: -1. OPTIMUS_AUTH_BASIC_TOKEN -2. OPTIMUS_AUTH_BEARER_TOKEN - -USAGE - optimus [flags] - -CORE COMMANDS - backup Backup a resource and its downstream - deploy Deploy current optimus project to server - job Interact with schedulable Job - replay Re-running jobs in order to update data for older dates/partitions - resource Interact with data resource - -DEV COMMANDS - serve Starts optimus service - -ADDITIONAL COMMANDS - completion generate the autocompletion script for the specified shell - config Manage optimus configuration required to deploy specifications - extension Operate with extension - help Help about any command - version Print the client version information - -FLAGS - --help Show help for command - --no-color Disable colored output - -EXAMPLES - $ optimus job create - $ optimus backup create - $ optimus backup list - $ optimus replay create - -LEARN MORE - Use 'optimus --help' for more information about a command. - Read the manual at https://odpf.github.io/optimus/ - -FEEDBACK - Open an issue here https://github.com/odpf/optimus/issues - ``` \ No newline at end of file diff --git a/docs/docs/rfcs/20220507_simplify_plugin_maintenance.md b/docs/docs/rfcs/20220507_simplify_plugin_maintenance.md index da7a0e5bb2..7944089e03 100644 --- a/docs/docs/rfcs/20220507_simplify_plugin_maintenance.md +++ b/docs/docs/rfcs/20220507_simplify_plugin_maintenance.md @@ -8,25 +8,33 @@ The scope of this rfc is to simplify the release and deployment operations w.r.t the optimus plugin ecosystem. The proposal here is to : -1. **Avoid Wrapping Executor Images** : -Decouple the executor_boot_process and the executor as separate containers where the airflow worker launches a pod with init-container (for boot process) adjacent to executor container. + +1. **Avoid Wrapping Executor Images** : + Decouple the executor_boot_process and the executor as separate containers where the airflow worker launches a pod with init-container (for boot process) adjacent to executor container. 2. **Simplfy Plugin Installation** : - - `Server end` : Install plugins declaratively at runtime instead of manually baking them into the optimus server image (in kubernetes setup). - - `Client end` : Plugin interface for client-end is limited to support Version, Survey Questions and Answers etc. that can be extracted out from the current plugin interface and maintained as yaml file which simplifies platform dependent plugin distribution for cli. + - `Server end` : Install plugins declaratively at runtime instead of manually baking them into the optimus server image (in kubernetes setup). + - `Client end` : Plugin interface for client-end is limited to support Version, Survey Questions and Answers etc. that can be extracted out from the current plugin interface and maintained as yaml file which simplifies platform dependent plugin distribution for cli. + # Technical Design + ## Background : + ### Changes that trigger a new release in Optimus setup: -* Executor Image changes -* Executor Image Wrapper changes -* Plugin binary changes -* Optimus binary changes + +- Executor Image changes +- Executor Image Wrapper changes +- Plugin binary changes +- Optimus binary changes ### Release dependencies as per current design -* `Executor Image release` -> `Executor Wrapper Image release` -> `Plugin binary release` -> `Server release` -* `Plugin binary release` -> `Server release` -### 1. Avoid Wrapping Executor Images : -* The `executor_boot_process` and `executor` are coupled: +- `Executor Image release` -> `Executor Wrapper Image release` -> `Plugin binary release` -> `Server release` +- `Plugin binary release` -> `Server release` + +### 1. Avoid Wrapping Executor Images : + +- The `executor_boot_process` and `executor` are coupled: + ``` -- Plugin repo structure /task/ @@ -35,14 +43,15 @@ Decouple the executor_boot_process and the executor as separate containers where ``` Executor Wrapper Image (Task Image) : + - It's a wrapper around the executor_image to facilitate boot mechanism for executor. - The optimus binary is downloaded during buildtime of this image. - During runtime, it does as follow : - - Fetch assets, secrets, env from optimus server. - - Load the env and launches the executor process. + - Fetch assets, secrets, env from optimus server. + - Load the env and launches the executor process. ``` -task_image +task_image | executor_image | optimus-bin | entrypoint.sh (load assets, env and launch executor) @@ -50,37 +59,44 @@ task_image The `optimus-bin` and `entrypoint.sh` are baked into the `task_image` and is being maintained by task/plugin developers. -### 2. Simplify Plugin Installation : -* Plugin binaries are manually installed (baked into optimus image in kubernetes setup). -* Any change in plugin code demands re-creation of optimus image with new plugin binary, inturn demanding redeployment of optimus server. (in kubernetes setup) -* At client side, plugin binaries require support for different platforms. +### 2. Simplify Plugin Installation : + +- Plugin binaries are manually installed (baked into optimus image in kubernetes setup). +- Any change in plugin code demands re-creation of optimus image with new plugin binary, inturn demanding redeployment of optimus server. (in kubernetes setup) +- At client side, plugin binaries require support for different platforms. ## Approach : + ### 1. Avoid Wrapping Executor Images : -* Decouple the lifecycle of the executor and the boot process as seperate containers/images. + +- Decouple the lifecycle of the executor and the boot process as seperate containers/images. Simplify Plugins Executor **Task Boot Sequence**: + 1. Airflow worker fetches env and secrets for the job and adds them to the executor pod as environment variables. 2. KubernetesPodOperator spawns init-container and executor-container, mounted with shared volume (type emptyDir) for assets. 3. `init-container` fetches assets, config, env files and writes onto the shared volume. 4. the default entrypoint in the executor-image starts the actual job. - ### 2. Simplify Plugin Installations : + #### A) Plugin Manager: -+ Currently the plugins are maintained as monorepo and versioned together. For any change in a single plugin, a new tar file containing all plugin binaries is created and released. -+ A plugin manager is required to support declarative installation of plugins so that plugins can be independently versioned, packaged and installed. -+ This plugin manager consumes a config (plugins_config) and downloads artifacts from a plugin repository. + +- Currently the plugins are maintained as monorepo and versioned together. For any change in a single plugin, a new tar file containing all plugin binaries is created and released. +- A plugin manager is required to support declarative installation of plugins so that plugins can be independently versioned, packaged and installed. +- This plugin manager consumes a config (plugins_config) and downloads artifacts from a plugin repository. + * Optimus support for plugin manager as below. - * `optimus plugin install -c config.yaml` -- at server + - `optimus plugin install -c config.yaml` -- at server * Support for different kinds of plugin repositories (like s3, gcs, url, local file system etc..) gives the added flexibility and options to distribute and install the plugin binaries in different ways. * Plugins are installed at container runtime and this decouples the building of optimus docker image from plugins installations. -* Example for the plugin_config: +* Example for the plugin_config: + ```yaml plugin: dir: .plugins @@ -88,25 +104,27 @@ plugin: # local filesystem for dev - ../transformers/dist/bq2bq_darwin_arm64/optimus-bq2bq_darwin_arm64 # any http uri - - https://github.com/odpf/optimus/releases/download/v0.2.5/optimus_0.2.5_linux_arm64.tar.gz - - ``` + - https://github.com/raystack/optimus/releases/download/v0.2.5/optimus_0.2.5_linux_arm64.tar.gz +``` + #### B) Yaml Plugin Interface: (for client side simplification) -+ Currently plugins are implemented and distributed as binaries and as clients needs to install them, it demands support for different host architectures. -+ Since CLI (client side) plugins just require details about plugin such as Version, Suevery Questions etc. the proposal here is to maintain CLI plugins as yaml files. -+ Implementation wise, the proposal here is to split the current plugin interface (which only supports interaction with binary plugins) to also accommodate yaml based plugins. -+ The above mentioned pluign manager, at server end, would be agnostic about the contents of plugin artifacts from the repository. -+ At client side, the CLI could sync the yaml files from the server to stay up-to-date with the server w.r.t plugins. -+ At this point, we have the scope to move away from binary plugins except for bq2bq plugin due to its depdendency on `ComplileAsset` and `ResolveDependency` functionalities which are required at server end (not at cli). -+ Handling Bq2Bq plugin: - + Move `CompileAsset` functionality as a part of Bq2bq executor. - + Move `ResolveDependency` functionality to optimus core which should support dependecy-resolution on standard-sql -+ Meanwhile the Bq2bq plugin is handled, the plugin interface can be maintanined in such a way that it also supports binary plugin in addition to yaml (as backward compaitbility feature). -+ The plugin discovery logic should be to load binary if present, else load yaml file; for a single plugin. -+ Now that we have yaml files at server, CLI can sync only the yaml files from the server. - * `optimus plugin sync -c optimus.yaml` - -* Example representation of the yaml plugin : + +- Currently plugins are implemented and distributed as binaries and as clients needs to install them, it demands support for different host architectures. +- Since CLI (client side) plugins just require details about plugin such as Version, Suevery Questions etc. the proposal here is to maintain CLI plugins as yaml files. +- Implementation wise, the proposal here is to split the current plugin interface (which only supports interaction with binary plugins) to also accommodate yaml based plugins. +- The above mentioned pluign manager, at server end, would be agnostic about the contents of plugin artifacts from the repository. +- At client side, the CLI could sync the yaml files from the server to stay up-to-date with the server w.r.t plugins. +- At this point, we have the scope to move away from binary plugins except for bq2bq plugin due to its depdendency on `ComplileAsset` and `ResolveDependency` functionalities which are required at server end (not at cli). +- Handling Bq2Bq plugin: + - Move `CompileAsset` functionality as a part of Bq2bq executor. + - Move `ResolveDependency` functionality to optimus core which should support dependecy-resolution on standard-sql +- Meanwhile the Bq2bq plugin is handled, the plugin interface can be maintanined in such a way that it also supports binary plugin in addition to yaml (as backward compaitbility feature). +- The plugin discovery logic should be to load binary if present, else load yaml file; for a single plugin. +- Now that we have yaml files at server, CLI can sync only the yaml files from the server. + - `optimus plugin sync -c optimus.yaml` + +* Example representation of the yaml plugin : + ```yaml name: bq2bq description: BigQuery to BigQuery transformation task @@ -116,7 +134,7 @@ pluginmods: - dependencyresolver pluginversion: 0.1.0-SNAPSHOT-27cb56f apiversion: [] -image: docker.io/odpf/optimus-task-bq2bq-executor:0.1.0-SNAPSHOT-27cb56f +image: docker.io/raystack/optimus-task-bq2bq-executor:0.1.0-SNAPSHOT-27cb56f secretpath: /tmp/auth.json dependson: [] hooktype: "" @@ -161,13 +179,12 @@ defaultassets: -- SQL query goes here Select * from "project.dataset.table"; - - ``` -## Result: -Simplify Plugins +``` -* Executor boot process is standardised and extracted away from plugin developers. Now any arbitrary image can be used for executors. -* At server side, for changes in plugin (dur to plugin release), update the plugin_manager_config and restart the optimus server pod. The plugin manager is expected to reinstall the plugins. -* Client side dependency on plugins is simplified with yaml based plugins. +## Result: +Simplify Plugins +- Executor boot process is standardised and extracted away from plugin developers. Now any arbitrary image can be used for executors. +- At server side, for changes in plugin (dur to plugin release), update the plugin_manager_config and restart the optimus server pod. The plugin manager is expected to reinstall the plugins. +- Client side dependency on plugins is simplified with yaml based plugins. diff --git a/docs/docs/roadmap.md b/docs/docs/roadmap.md deleted file mode 100644 index cf8b9f8b5b..0000000000 --- a/docs/docs/roadmap.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -id: roadmap -title: Roadmap -slug: /roadmap ---- - -This is a live document which gives an idea for all the users of Optimus on what are we upto. - -### Secret Management in Optimus -As optimus meant to deal with various warehouse systems & with the plugin support provides the capability to interact with other third party systems. -It brings a need for proper secret management to store & use securely for all users onboard. - -### Test Your Jobs -Giving a provision for users to test the jobs before deploying helps users with faster feedbacks. - -### Telemetry -With proper monitoring we can get many insights into Optimus, which helps in debugging any failures. May not be a direct end user feature but this is very important. - -### Add More Plugins -Once the data is analyzed in warehouse, there is always a need for getting the data out of the system for visualizations or for consumption. This is a constant effort to improve the ecosystem that optimus supports. -The plugins that we will be adding support is to pull data from BQ to Kafka, JDBC, FTP. - -### Task Versioning -Versioning of tasks comes to handy when there is a time significance associated to task. -On replay, an older version of task has to run which was active at that time and the newer version on the coming days. - -### Improved Window Support -The current windowing which is used by for automated dependency resoultion & the macros which are derived from it are being used for input data filtering is little confusing & limiting in nature. -This will be an effort to easy the same. - -### SLA Tracking -Giving a provision for defining the SLAs & providing a dashboard to visualize how the slas are met is a must. -With this users will be able to monitor any slas that are breached out of the box. - -### SQLite Support -With support of SQLlite database, just helps users to kick start Optimus fast & easy to try on without having a dependency on postgres. - -### Custom Macro Support -Custom Macros will unleash many capabilities, this will help users to template their queries to avoid any duplication. - -### Inbuilt Testing F/w -Currently Optimus relies on Predator for Quality Checks, instead of relying on predator which is not extensible & supports only BQ, providing a capability to test the job runs directly. - -### Inter Task/Hook Communication -As we scale, there are situations where tasks & hooks has to share information directly instead of relying on another system. This opens up many capabilities. diff --git a/docs/docs/server-guide/configuration.md b/docs/docs/server-guide/configuration.md new file mode 100644 index 0000000000..1be99d983d --- /dev/null +++ b/docs/docs/server-guide/configuration.md @@ -0,0 +1,20 @@ +# Server Configuration +See the server configuration example on config.sample.yaml. + +| Configuration | Description | +|------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Log | Logging level & format configuration. | +| Serve | Represents any configuration needed to start Optimus, such as port, host, DB details, and application key (for secrets encryption). | +| Scheduler | Any scheduler-related configuration. Currently, Optimus only supports Airflow and has been set to default. | +| Telemetry | Can be used for tracking and debugging using Jaeger. | +| Plugin | Optimus will try to look for the plugin artifacts through this configuration. | +| Resource Manager | If your server has jobs that are dependent on other jobs in another server, you can add that external Optimus server host as a resource manager. | + +_Note:_ + +Application key can be randomly generated using: +```shell +head -c 50 /dev/random | base64 +``` +Just take the first 32 characters of the string. + diff --git a/docs/docs/server-guide/db-migrations.md b/docs/docs/server-guide/db-migrations.md new file mode 100644 index 0000000000..a3dd166970 --- /dev/null +++ b/docs/docs/server-guide/db-migrations.md @@ -0,0 +1,34 @@ +# DB Migrations + +## Migrate to Specific Version +Upgrade the DB to the specified migration version. +```shell +$ optimus migration to --version [version] +``` +_Note: To migrate to the latest one, running the Optimus’ serve command should be enough. It will migrate to the latest +automatically._ + +## Rollback +Revert the current active migration to several previous migration versions. +```shell +$ optimus migration rollback --count [n] +``` +[n] is the number of migrations to rollback. + +## Export & Upload +In some cases, due to differences in how Optimus is storing the job and resource specifications in DB in 1 version to +another version, you might want to export the jobs in the server to your local YAML files and redeploy it. + +The export is possible using the following command: +```shell +$ optimus job export –-dir job_result +``` + +This means, you will export all of the jobs from all of the projects in your Optimus server to the job_result directory. It is also possible to run this export command against a single project/namespace/job. + +For exporting resources, use the following command: +```shell +$ optimus resource export -dir resource_result +``` + +Similar to the job command, it is also possible to export resources only for a single project/namespace/resource. diff --git a/docs/docs/server-guide/installing-plugins.md b/docs/docs/server-guide/installing-plugins.md new file mode 100644 index 0000000000..2605d3b728 --- /dev/null +++ b/docs/docs/server-guide/installing-plugins.md @@ -0,0 +1,31 @@ +# Installing Plugins +Plugin needs to be installed in the Optimus server before it can be used. Optimus uses the following directories for +discovering plugin binaries + +``` +./.plugins +./ +/ +/.optimus/plugins +$HOME/.optimus/plugins +/usr/bin +/usr/local/bin +``` + +Even though the above list of directories is involved in plugin discovery, it is advised to use .plugins in the +current working directory of the project or Optimus binary. + +To simplify installation, you can add plugin artifacts in the server config: +```yaml +plugin: + artifacts: + - https://...path/to/optimus-plugin-neo.yaml # http + - http://.../plugins.zip # zip + - ../transformers/optimus-bq2bq_darwin_arm64 # relative paths + - ../transformers/optimus-plugin-neo.yaml +``` + +Run below command to auto-install the plugins in the `.plugins` directory. +```shell +$ optimus plugin install -c config.yaml # This will install plugins in the `.plugins` folder. +``` diff --git a/docs/docs/server-guide/starting-optimus-server.md b/docs/docs/server-guide/starting-optimus-server.md new file mode 100644 index 0000000000..10d4e65377 --- /dev/null +++ b/docs/docs/server-guide/starting-optimus-server.md @@ -0,0 +1,54 @@ +# Starting Optimus Server +Starting a server requires server configuration, which can be loaded from file (use --config flag), environment +variable `OPTIMUS_[CONFIGNAME]`, or config.yaml file in Optimus binary directory. + +**1. Using --config flag** + ```shell + $ optimus serve --config /path/to/config/file.yaml + ``` + +If you specify the configuration file using the --config flag, then any configs defined in the env variable and default +config.yaml from the Optimus binary directory will not be loaded. + +**2. Using environment variable** + +All the configs can be passed as environment variables using `OPTIMUS_[CONFIG_NAME]` convention. [CONFIG_NAME] is the +key name of config using `_` as the path delimiter to concatenate between keys. + +For example, to use the environment variable, assuming the following configuration layout: + +```yaml +version: 1 +serve: + port: 9100 + app_key: randomhash +``` + + +Here is the corresponding environment variable for the above + +| Configuration key | Environment variable | +|--------------------|-----------------------| +| version | OPTIMUS_VERSION | +| serve.port | OPTIMUS_PORT | +| serve.app_key | OPTIMUS_SERVE_APP_KEY | + + + +Set the env variable using export +```shell +$ export OPTIMUS_PORT=9100 +``` + + +Note: If you specify the env variable and you also have config.yaml in the Optimus binary directory, then any configs +from the env variable will override the configs defined in config.yaml in Optimus binary directory. + + +**3. Using default config.yaml from Optimus binary directory** +```shell +$ which optimus +/usr/local/bin/optimus +``` + +So the config.yaml file can be loaded on /usr/local/bin/config.yaml diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index da84181962..c0da5a35c8 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -5,13 +5,13 @@ const darkCodeTheme = require('prism-react-renderer/themes/dracula'); module.exports = { title: 'Optimus', tagline: 'Performant data workflow orchestrator', - url: 'https://odpf.github.io', + url: 'https://raystack.github.io', baseUrl: '/optimus/', onBrokenLinks: 'throw', // trailingSlash: true, onBrokenMarkdownLinks: 'warn', favicon: 'img/favicon.ico', - organizationName: 'odpf', + organizationName: 'raystack', projectName: 'optimus', themeConfig: { @@ -41,7 +41,7 @@ module.exports = { className: 'header-slack-link', }, { - href: 'https://github.com/odpf/optimus', + href: 'https://github.com/raystack/optimus', className: 'navbar-item-github', position: 'right', }, @@ -53,10 +53,10 @@ module.exports = { { title: 'Products', items: [ - { label: 'Meteor', href: 'https://github.com/odpf/meteor' }, - { label: 'Firehose', href: 'https://github.com/odpf/firehose' }, - { label: 'Raccoon', href: 'https://github.com/odpf/raccoon' }, - { label: 'Dagger', href: 'https://odpf.github.io/dagger/' }, + { label: 'Meteor', href: 'https://github.com/raystack/meteor' }, + { label: 'Firehose', href: 'https://github.com/raystack/firehose' }, + { label: 'Raccoon', href: 'https://github.com/raystack/raccoon' }, + { label: 'Dagger', href: 'https://raystack.github.io/dagger/' }, ], }, { @@ -71,11 +71,11 @@ module.exports = { title: 'Community', items: [ { label: 'Slack', href: 'https://bit.ly/2RzPbtn' }, - { label: 'GitHub', href: 'https://github.com/odpf/optimus' } + { label: 'GitHub', href: 'https://github.com/raystack/optimus' } ], }, ], - copyright: `Copyright © 2020-${new Date().getFullYear()} ODPF`, + copyright: `Copyright © 2020-${new Date().getFullYear()} raystack`, }, prism: { theme: lightCodeTheme, @@ -83,7 +83,7 @@ module.exports = { }, announcementBar: { id: 'star-repo', - content: '⭐️ If you like Optimus, give it a star on GitHub! ⭐', + content: '⭐️ If you like Optimus, give it a star on GitHub! ⭐', backgroundColor: '#222', textColor: '#eee', isCloseable: true, @@ -101,12 +101,12 @@ module.exports = { showLastUpdateAuthor: true, showLastUpdateTime: true, sidebarPath: require.resolve('./sidebars.js'), - editUrl: 'https://github.com/odpf/optimus/edit/master/docs/', + editUrl: 'https://github.com/raystack/optimus/edit/master/docs/', }, blog: { showReadingTime: true, editUrl: - 'https://github.com/odpf/optimus/edit/master/docs/blog/', + 'https://github.com/raystack/optimus/edit/master/docs/blog/', }, theme: { customCss: [ diff --git a/docs/sidebars.js b/docs/sidebars.js index f50cfd5ab1..7cbc46d04c 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -20,55 +20,82 @@ module.exports = { label: "Getting Started", items: [ "getting-started/installation", - "getting-started/configuration" + "getting-started/quick-start" ], collapsed: false, }, { type: "category", - label: "Guides", + label: "Concept", items: [ - "guides/create-job", - "guides/adding-hook", - "guides/create-bigquery-dataset", - "guides/create-bigquery-table", - "guides/create-bigquery-view", - "guides/create-bigquery-external-table", - "guides/organising-specifications", - "guides/optimus-serve", - "guides/task-bq2bq", - "guides/extension", - "guides/backup", - "guides/replay", - "guides/secret", - "guides/refresh-jobs", - "guides/alerts" + "concepts/architecture", + "concepts/project", + "concepts/namespace", + "concepts/resource", + "concepts/job", + "concepts/job-run", + "concepts/dependency", + "concepts/macros", + "concepts/intervals-and-windows", + "concepts/secret", + "concepts/plugin", + "concepts/replay-and-backup", ], }, { type: "category", - label: "Concepts", + label: "Server Guide", items: [ - "concepts/overview", - "concepts/architecture", - "concepts/intervals-and-windows", + "server-guide/configuration", + "server-guide/installing-plugins", + "server-guide/starting-optimus-server", + "server-guide/db-migrations", ], }, { type: "category", - label: "Development", - items: ["development/building-plugin"], + label: "Client Guide", + items: [ + "client-guide/configuration", + "client-guide/managing-project-namespace", + "client-guide/managing-secrets", + "client-guide/installing-plugin", + "client-guide/manage-bigquery-resource", + "client-guide/create-job-specifications", + "client-guide/setting-up-alert", + "client-guide/verifying-jobs", + "client-guide/applying-job-specifications", + "client-guide/uploading-jobs-to-scheduler", + "client-guide/organizing-specifications", + "client-guide/backup-bigquery-resource", + "client-guide/replay-a-job", + "client-guide/work-with-extension", + ], + }, + { + type: "category", + label: "Building a Plugin", + items: [ + "building-plugin/introduction", + "building-plugin/tutorial", + ], }, { type: "category", label: "Contribute", - items: ["contribute/contributing"], + items: [ + "contribute/contribution-process", + "contribute/developer-env-setup", + ], }, - 'roadmap', { type: "category", label: "Reference", - items: ["reference/api", "reference/faq", "reference/shell-autocompletion"], + items: [ + "reference/api", + "reference/metrics", + "reference/faq", + ], }, ], }; diff --git a/docs/src/pages/help.js b/docs/src/pages/help.js index b10a5f40e4..408970dcde 100644 --- a/docs/src/pages/help.js +++ b/docs/src/pages/help.js @@ -25,7 +25,7 @@ export default function Home() {
The Optimus team has an open source slack workspace to discuss development and support. Most of the Optimus discussions happen in #optimus channel. -
Join us on Slack +
Join us on Slack
) }, { @@ -33,7 +33,7 @@ export default function Home() { content: (
Have a general issue or bug that you've found? We'd love to hear about it in our GitHub issues. This can be feature requests too! -
Go to issues +
Go to issues
) }, @@ -42,7 +42,7 @@ export default function Home() { content: (
For help and questions about best practices, join our GitHub discussions. Browse and ask questions. -
Go to discussions +
Go to discussions
) } diff --git a/docs/src/pages/index.js b/docs/src/pages/index.js index 8b687df46a..5013e7b769 100644 --- a/docs/src/pages/index.js +++ b/docs/src/pages/index.js @@ -20,7 +20,7 @@ const Hero = () => {
-
+
diff --git a/docs/static/img/banner-0.6.png b/docs/static/img/banner-0.6.png new file mode 100644 index 0000000000000000000000000000000000000000..316a581e78ad1aca0604d2bd29ceff5b9a543a77 GIT binary patch literal 267500 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�UDM?B>Ra#I9qLCJ)*4#M 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

Jn45%Kg>Gt-6oS6A{1Ok(J)nb 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 z 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 Yom+zUzWB1j1O85*JagjpF}qv;2L&4?1ONa4 literal 0 HcmV?d00001 diff --git a/docs/static/img/docs/Concept_JobRun.png b/docs/static/img/docs/Concept_JobRun.png new file mode 100644 index 0000000000000000000000000000000000000000..df196c45d16b2d3349f6571fda3f47758f292260 GIT binary patch literal 130268 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� zLMTra_T| zUSpu-Ue4Q%Q-xI7$>Rg+MuXM>^kwH-VrGd($kU@%8g6o_=|Z=I0$9UW15>Dubp7Qg zV{G*BXQlvuZ+#szvd7;|?NLE6&qnwXNMEkZ$Dda*VLq~vHWD^zd8QdFP=k?W>+_*2 zJ)7S&PskZjw;pDH^M%9&qpHEzB3f^$yDjXXPBgw4Ujt<|HNm*x2y=c#l$wnKM*dvp*P3L3oa+9z@7Zbktf@m zFO1Vz2^zl49czJ)BX_#;6!IU1pVL=5?H(NUWOno$X@zQVo@I*2|13H$I)|eZ>~*zw zCUpxhV;xyl88@VUnXD8&?rIF;+UssF#=R-^`4?*anY%p#q!pPHi)%#X&omgGLUp@##q&ceR zK~3#v8Ta(E5Q{L~%(}h{hE69`nN|YqD&*xg(Cp_8)7Iusriv$_DU1n(ZJQwpZTlVsBWAce3MM_Sa@l{hD0?$geWN!A+Kkxb3YQ?=vH~&wxwXR*+#Mv&h81=GEB@T~g*REobr+-pg4ko8ARafx z#>JO#6bQ3a0)*>|uCteulA5=a<(K8y4C`y&$GDN+{O|^zHGIhoLAdIe?z_IZBEA4j zts0Gk|&(_6oe&s3|Ceq@C+zV`#$ltO4K4yK}R$rR0sq{^Fp*xD#MTJcO|bL6U9 zSh2<#Sa7V{jL@?Vn){N=Ec_2i&g13_-$n_8u8O?;=$fGuGh4M2!Tm+idV zN5cRDJ`}^;OzJvR7;r|&^*wSl1wW1Hg7E}Q>%`XEzQBGZ|4cJ+D)ir-E(b($e zBA)ApT*2r2RkeRu33j}dK5i9qytMOiz#9&N3p~AGK_yxoX|uvwUWJ?VX^7BQ_*Y?e-$7p>jKL zJn?O;ay&*;mgQhlk}yZJzbyvEdJCcVvUmp-u3Pg)4Rs^9wosrTT<=6PfGZ~lKlz2L zR2k}?H$3)X9fzYCW!_6k>9^W^`%B^NXKozQkBLU6#}KkS{&vw#uFF!xY{vf*7< zf8@uY@bM;BTg0%gTSvq&x9ztNM`PfDxu5;P6uoGEZReJsby=##=~})U{46-&F-Oof zV5_1M;f&gqY(a~e3k*7$;%5~Yx5L%^(;Fyv20+a5ZM@>~JJklA>9RN$GV!df$Onz5 zPc)`k4!%99#+;EPlDHG~nbpSQ1&ecvrCyMPDbIHqYf!7gep5uoub$7Y06ynnXJjAup(zi`5bPk3)>41?4QC*58V zjrI?nHr5sVant6%nq-`oL3nKFYe(d0Q5Y{E#SFJK#J+uVJTPd<dW@n1jz~?@%VozH%f%hwZY%#BrBgsBFrTH;H1WeDB6rfpZ|5 zc^z^CDo*{G`R*!Q7b?ipq5V?>=B5Jn9Z&rjUAQd8AMqBH!O{LHZTz%ls;T{C>CP

h&t4t$}zFTQSPN>Wi#v0UWMHN-*a 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 GNB;-oS?fCh literal 0 HcmV?d00001 diff --git a/docs/static/img/docs/CreateJobSpecFlow.png b/docs/static/img/docs/CreateJobSpecFlow.png new file mode 100644 index 0000000000000000000000000000000000000000..3c552d77ecfa1b8f31b606517ee00d8bc279a657 GIT binary patch literal 133519 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≦AbwsjXG32EWPTX#TTepaPZ;6= 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

TKNt%W(6xnJ&|@@o_%p=%5w`>ytV< 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 Dd#)r{ literal 0 HcmV?d00001 diff --git a/docs/static/img/docs/OptimusArchitecture_dark_07June2021.png b/docs/static/img/docs/OptimusArchitecture_dark_07June2021.png deleted file mode 100644 index c726ce62ec99b0bf9edc17a96a472a29f0f7c00a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 403725 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%0AsZPz=Lhp00Kvoq!+B{oZY0ikT^ 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

nR|0>O0xy|XhnJM{@2;WdI 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

mCOk+nP6b#pp9Q$SlF1pS7OTQ-qzY~$gpF5k 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 Gd4B?ghXgzT literal 0 HcmV?d00001 diff --git a/docs/static/img/docs/ReplaySequential.png b/docs/static/img/docs/ReplaySequential.png new file mode 100644 index 0000000000000000000000000000000000000000..9b9c663ac01b2dadff0389c3e2f7f09db4eac0ea GIT binary patch literal 27206 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 str: expr_mapping = { '@yearly': '0 0 1 1 *', @@ -263,7 +256,7 @@ def _are_all_job_runs_successful(self, schedule_time_window_start, schedule_time self.log.info("job_run api response :: {}".format(api_response)) except Exception as e: self.log.warning("error while fetching job runs :: {}".format(e)) - return False + raise AirflowFailException(e) for job_run in api_response['jobRuns']: if job_run['state'] != 'success': self.log.info("failed for run :: {}".format(job_run)) @@ -307,9 +300,19 @@ def _xcom_value_has_error(_xcom) -> bool: if SCHEDULER_ERR_MSG in event_meta.keys(): failure_message = failure_message + ", " + event_meta[SCHEDULER_ERR_MSG] - print("failures: {}".format(failure_message)) + if len(failure_message)>0: + log.info(f'failures: {failure_message}') task_instance = context.get('task_instance') + + if event_meta["event_type"] == "TYPE_FAILURE" : + dag_run = context['dag_run'] + tis = dag_run.get_task_instances() + for ti in tis: + if ti.state == TaskInstanceState.FAILED: + task_instance = ti + break + message = { "log_url": task_instance.log_url, "task_id": task_instance.task_id, @@ -329,7 +332,7 @@ def _xcom_value_has_error(_xcom) -> bool: # post event log.info(event) resp = optimus_client.notify_event(params["project_name"], params["namespace"], params["job_name"], event) - print("posted event ", params, event, resp) + log.info(f'posted event {params}, {event}, {resp} ') return def get_run_type(context): @@ -394,9 +397,6 @@ def operator_start_event(context): def operator_success_event(context): try: run_type = get_run_type(context) - if run_type == "SENSOR": - print("clearing sensor xcom") - cleanup_xcom(context) meta = { "event_type": "TYPE_{}_SUCCESS".format(run_type), "status": "success" @@ -409,9 +409,6 @@ def operator_success_event(context): def operator_retry_event(context): try: run_type = get_run_type(context) - if run_type == "SENSOR": - print("clearing sensor xcom") - cleanup_xcom(context) meta = { "event_type": "TYPE_{}_RETRY".format(run_type), "status": "retried" @@ -424,9 +421,6 @@ def operator_retry_event(context): def operator_failure_event(context): try: run_type = get_run_type(context) - if run_type == "SENSOR": - print("clearing sensor xcom") - cleanup_xcom(context) meta = { "event_type": "TYPE_{}_FAIL".format(run_type), "status": "failed" @@ -453,7 +447,8 @@ def optimus_sla_miss_notify(dag, task_list, blocking_task_list, slas, blocking_t sla_list.append({ 'task_id': sla.task_id, 'dag_id': sla.dag_id, - 'scheduled_at': sla.execution_date.strftime(TIMESTAMP_FORMAT), + 'scheduled_at' : dag.following_schedule(sla.execution_date).strftime(TIMESTAMP_FORMAT), + 'airflow_execution_time': sla.execution_date.strftime(TIMESTAMP_FORMAT), 'timestamp': sla.timestamp.strftime(TIMESTAMP_FORMAT) }) @@ -470,23 +465,19 @@ def optimus_sla_miss_notify(dag, task_list, blocking_task_list, slas, blocking_t } # post event resp = optimus_client.notify_event(params["project_name"], params["namespace"], params["job_name"], event) - print("posted event ", params, event, resp) + log.info(f'posted event {params}, {event}, {resp}') return except Exception as e: print(e) def shouldSendSensorStartEvent(ctx): try: - ti = ctx.get('task_instance') - key = "sensorEvt/{}/{}/{}".format(ti.task_id , ctx.get('next_execution_date').strftime(TIMESTAMP_FORMAT) , ti.try_number) - - result = ti.xcom_pull(key=key) - if not result: - print("sending NEW sensor start event for attempt number ", ti.try_number) - ti.xcom_push(key=key, value=True) + ti=ctx['ti'] + task_reschedules = TaskReschedule.find_for_task_instance(ti) + if len(task_reschedules) == 0 : + log.info(f'sending NEW sensor start event for attempt number-> {ti.try_number}') return True - print("ignoring sending sensor start event as its already sent") - return False + log.info("ignoring sending sensor start event as its not first poke") except Exception as e: print(e) @@ -495,23 +486,13 @@ def get_result_for_monitoring_from_xcom(ctx): ti = ctx.get('task_instance') return_value = ti.xcom_pull(key='return_value') except Exception as e: - print(f'error getting result for monitoring: {e}') + log.info(f'error getting result for monitoring: {e}') if type(return_value) is dict: if 'monitoring' in return_value: return return_value['monitoring'] return None -def cleanup_xcom(ctx): - try: - from airflow import settings - session = settings.Session() - ti = ctx.get('task_instance') - key = "sensorEvt/{}/{}/{}".format(ti.task_id , ctx.get('next_execution_date').strftime(TIMESTAMP_FORMAT) , ti.try_number) - session.query(XCom).filter(XCom.key == key).delete() - except Exception as e: - print(e) - # everything below this is here for legacy reasons, should be cleaned up in future def alert_failed_to_slack(context): @@ -554,7 +535,8 @@ def _xcom_value_has_error(_xcom) -> bool: if _xcom_value_has_error(xcom): failure_messages.append(xcom.value['error']) failure_message = ", ".join(failure_messages) - print("failures: {}".format(failure_message)) + if failure_message != "": + log.info(f'failures: {failure_message}') message_body = "\n".join([ "• *DAG*: {}".format(current_dag_id), diff --git a/ext/scheduler/airflow/airflow.go b/ext/scheduler/airflow/airflow.go index d70af4273d..9ae542820f 100644 --- a/ext/scheduler/airflow/airflow.go +++ b/ext/scheduler/airflow/airflow.go @@ -13,14 +13,16 @@ import ( "time" "github.com/kushsharma/parallel" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "gocloud.dev/blob" "gocloud.dev/gcerrors" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" - "github.com/odpf/optimus/internal/lib/cron" + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/lib/cron" + "github.com/raystack/optimus/internal/telemetry" ) //go:embed __lib.py @@ -30,7 +32,9 @@ const ( EntityAirflow = "Airflow" dagStatusBatchURL = "api/v1/dags/~/dagRuns/list" + dagURL = "api/v1/dags/%s" dagRunClearURL = "api/v1/dags/%s/clearTaskInstances" + dagRunCreateURL = "api/v1/dags/%s/dagRuns" airflowDateFormat = "2006-01-02T15:04:05+00:00" schedulerHostKey = "SCHEDULER_HOST" @@ -39,13 +43,17 @@ const ( jobsDir = "dags" jobsExtension = ".py" - concurrentTicketPerSec = 40 - concurrentLimit = 600 + concurrentTicketPerSec = 50 + concurrentLimit = 100 + + metricJobUpload = "job_upload_total" + metricJobRemoval = "job_removal_total" + metricJobStateSuccess = "success" + metricJobStateFailed = "failed" ) type Bucket interface { WriteAll(ctx context.Context, key string, p []byte, opts *blob.WriterOptions) error - // ReadAll(ctx context.Context, key string) ([]byte, error) List(opts *blob.ListOptions) *blob.ListIterator Delete(ctx context.Context, key string) error Close() error @@ -106,10 +114,20 @@ func (s *Scheduler) DeployJobs(ctx context.Context, tenant tenant.Tenant, jobs [ }(job)) } + countDeploySucceed := 0 + countDeployFailed := 0 for _, result := range runner.Run() { - multiError.Append(result.Err) + if result.Err != nil { + countDeployFailed++ + multiError.Append(result.Err) + continue + } + countDeploySucceed++ } - return errors.MultiToError(multiError) + raiseSchedulerMetric(tenant, metricJobUpload, metricJobStateSuccess, countDeploySucceed) + raiseSchedulerMetric(tenant, metricJobUpload, metricJobStateFailed, countDeployFailed) + + return multiError.ToErr() } // TODO list jobs should not refer from the scheduler, rather should list from db and it has nothing to do with scheduler. @@ -151,33 +169,41 @@ func (s *Scheduler) DeleteJobs(ctx context.Context, t tenant.Tenant, jobNames [] if err != nil { return err } - multiError := errors.NewMultiError("ErrorsInDeleteJobs") + me := errors.NewMultiError("ErrorsInDeleteJobs") + countDeleteJobsSucceed := 0 + countDeleteJobsFailed := 0 for _, jobName := range jobNames { if strings.TrimSpace(jobName) == "" { - multiError.Append(errors.InvalidArgument(EntityAirflow, "job name cannot be an empty string")) + me.Append(errors.InvalidArgument(EntityAirflow, "job name cannot be an empty string")) continue } blobKey := pathFromJobName(jobsDir, t.NamespaceName().String(), jobName, jobsExtension) if err := bucket.Delete(spanCtx, blobKey); err != nil { // ignore missing files if gcerrors.Code(err) != gcerrors.NotFound { - multiError.Append(err) + countDeleteJobsFailed++ + me.Append(err) } + continue } + countDeleteJobsSucceed++ } + raiseSchedulerMetric(t, metricJobRemoval, metricJobStateSuccess, countDeleteJobsSucceed) + raiseSchedulerMetric(t, metricJobRemoval, metricJobStateFailed, countDeleteJobsFailed) + err = deleteDirectoryIfEmpty(ctx, t.NamespaceName().String(), bucket) if err != nil { if gcerrors.Code(err) != gcerrors.NotFound { - multiError.Append(err) + me.Append(err) } } - return errors.MultiToError(multiError) + return me.ToErr() } // deleteDirectoryIfEmpty remove jobs Folder if it exists func deleteDirectoryIfEmpty(ctx context.Context, nsDirectoryIdentifier string, bucket Bucket) error { spanCtx, span := startChildSpan(ctx, "deleteDirectoryIfEmpty") - span.End() + defer span.End() jobsDir := pathForJobDirectory(jobsDir, nsDirectoryIdentifier) @@ -235,11 +261,11 @@ func (s *Scheduler) GetJobRuns(ctx context.Context, tnnt tenant.Tenant, jobQuery dagRunRequest := getDagRunRequest(jobQuery, jobCron) reqBody, err := json.Marshal(dagRunRequest) if err != nil { - return nil, err + return nil, errors.Wrap(EntityAirflow, "unable to marshal dag run request", err) } req := airflowRequest{ - URL: dagStatusBatchURL, + path: dagStatusBatchURL, method: http.MethodPost, body: reqBody, } @@ -251,17 +277,58 @@ func (s *Scheduler) GetJobRuns(ctx context.Context, tnnt tenant.Tenant, jobQuery resp, err := s.client.Invoke(spanCtx, req, schdAuth) if err != nil { - return nil, fmt.Errorf("failure reason for fetching airflow dag runs: %w", err) + return nil, errors.Wrap(EntityAirflow, "failure while fetching airflow dag runs", err) } var dagRunList DagRunListResponse if err := json.Unmarshal(resp, &dagRunList); err != nil { - return nil, fmt.Errorf("json error on parsing airflow dag runs: %s: %w", string(resp), err) + return nil, errors.Wrap(EntityAirflow, fmt.Sprintf("json error on parsing airflow dag runs: %s", string(resp)), err) } return getJobRuns(dagRunList, jobCron) } +// UpdateJobState set the state of jobs as enabled / disabled on scheduler +func (s *Scheduler) UpdateJobState(ctx context.Context, tnnt tenant.Tenant, jobNames []job.Name, state string) error { + spanCtx, span := startChildSpan(ctx, "UpdateJobState") + defer span.End() + + var data []byte + switch state { + case "enabled": + data = []byte(`{"is_paused": false}`) + case "disabled": + data = []byte(`{"is_paused": true}`) + } + + schdAuth, err := s.getSchedulerAuth(ctx, tnnt) + if err != nil { + return err + } + ch := make(chan error, len(jobNames)) + for _, jobName := range jobNames { + go func(jobName job.Name) { + req := airflowRequest{ + path: fmt.Sprintf(dagURL, jobName), + method: http.MethodPatch, + body: data, + } + _, err := s.client.Invoke(spanCtx, req, schdAuth) + ch <- err + }(jobName) + } + me := errors.NewMultiError("update job state on scheduler") + for i := 0; i < len(jobNames); i++ { + me.Append(<-ch) + } + + if len(me.Errors) > 0 { + return errors.Wrap(EntityAirflow, "failure while updating dag status", me.ToErr()) + } + + return nil +} + func getDagRunRequest(jobQuery *scheduler.JobRunsCriteria, jobCron *cron.ScheduleSpec) DagRunRequest { if jobQuery.OnlyLastRun { return DagRunRequest{ @@ -318,9 +385,32 @@ func (s *Scheduler) ClearBatch(ctx context.Context, tnnt tenant.Tenant, jobName startExecutionTime.UTC().Format(airflowDateFormat), endExecutionTime.UTC().Format(airflowDateFormat))) req := airflowRequest{ - URL: dagRunClearURL, + path: fmt.Sprintf(dagRunClearURL, jobName.String()), + method: http.MethodPost, + body: data, + } + schdAuth, err := s.getSchedulerAuth(ctx, tnnt) + if err != nil { + return err + } + _, err = s.client.Invoke(spanCtx, req, schdAuth) + if err != nil { + return errors.Wrap(EntityAirflow, "failure while clearing airflow dag runs", err) + } + return nil +} + +func (s *Scheduler) CreateRun(ctx context.Context, tnnt tenant.Tenant, jobName scheduler.JobName, executionTime time.Time, dagRunIDPrefix string) error { + spanCtx, span := startChildSpan(ctx, "CreateRun") + defer span.End() + + data := []byte(fmt.Sprintf(`{"dag_run_id": %q, "execution_date": %q}`, + fmt.Sprintf("%s__%s", dagRunIDPrefix, executionTime.UTC().Format(airflowDateFormat)), + executionTime.UTC().Format(airflowDateFormat)), + ) + req := airflowRequest{ + path: fmt.Sprintf(dagRunCreateURL, jobName.String()), method: http.MethodPost, - param: jobName.String(), body: data, } schdAuth, err := s.getSchedulerAuth(ctx, tnnt) @@ -329,7 +419,7 @@ func (s *Scheduler) ClearBatch(ctx context.Context, tnnt tenant.Tenant, jobName } _, err = s.client.Invoke(spanCtx, req, schdAuth) if err != nil { - return fmt.Errorf("failure reason for clearing airflow dag runs: %w", err) + return errors.Wrap(EntityAirflow, "failure while creating airflow dag run", err) } return nil } @@ -344,3 +434,11 @@ func NewScheduler(l log.Logger, bucketFac BucketFactory, client Client, compiler secretGetter: secretGetter, } } + +func raiseSchedulerMetric(jobTenant tenant.Tenant, metricName, status string, metricValue int) { + telemetry.NewCounter(metricName, map[string]string{ + "project": jobTenant.ProjectName().String(), + "namespace": jobTenant.NamespaceName().String(), + "status": status, + }).Add(float64(metricValue)) +} diff --git a/ext/scheduler/airflow/bucket/factory.go b/ext/scheduler/airflow/bucket/factory.go index eb2ffbce23..17715524f9 100644 --- a/ext/scheduler/airflow/bucket/factory.go +++ b/ext/scheduler/airflow/bucket/factory.go @@ -7,9 +7,9 @@ import ( "gocloud.dev/blob/fileblob" "gocloud.dev/blob/memblob" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/ext/scheduler/airflow" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/ext/scheduler/airflow" + "github.com/raystack/optimus/internal/errors" ) const ( diff --git a/ext/scheduler/airflow/bucket/gcs.go b/ext/scheduler/airflow/bucket/gcs.go index 607a44efae..29b6d154b3 100644 --- a/ext/scheduler/airflow/bucket/gcs.go +++ b/ext/scheduler/airflow/bucket/gcs.go @@ -12,8 +12,8 @@ import ( "gocloud.dev/gcp" "golang.org/x/oauth2/google" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/ext/scheduler/airflow" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/ext/scheduler/airflow" ) const ( diff --git a/ext/scheduler/airflow/client.go b/ext/scheduler/airflow/client.go index 6ec58402cb..b98b34f0d6 100644 --- a/ext/scheduler/airflow/client.go +++ b/ext/scheduler/airflow/client.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "encoding/base64" - "errors" "fmt" "io" "net/http" @@ -15,20 +14,19 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/trace" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/internal/lib/cron" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/lib/cron" ) const ( - pageLimit = 99999 - dagStatusURL = "api/v1/dags/%s/dagRuns" + pageLimit = 99999 ) type airflowRequest struct { - URL string + path string method string body []byte - param string } type DagRunListResponse struct { @@ -67,20 +65,21 @@ func NewAirflowClient() *ClientAirflow { func (ac ClientAirflow) Invoke(ctx context.Context, r airflowRequest, auth SchedulerAuth) ([]byte, error) { var resp []byte - request, err := http.NewRequestWithContext(ctx, r.method, buildEndPoint(auth.host, r.URL, r.param), bytes.NewBuffer(r.body)) + endpoint := buildEndPoint(auth.host, r.path) + request, err := http.NewRequestWithContext(ctx, r.method, endpoint, bytes.NewBuffer(r.body)) if err != nil { - return resp, fmt.Errorf("failed to build http request for %s due to %w", r.URL, err) + return resp, fmt.Errorf("failed to build http request for %s due to %w", endpoint, err) } request.Header.Set("Content-Type", "application/json") request.Header.Set("Authorization", fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(auth.token)))) httpResp, respErr := ac.client.Do(request) if respErr != nil { - return resp, fmt.Errorf("failed to call airflow %s due to %w", r.URL, respErr) + return resp, fmt.Errorf("failed to call airflow %s due to %w", endpoint, respErr) } if httpResp.StatusCode != http.StatusOK { httpResp.Body.Close() - return resp, fmt.Errorf("status code received %d on calling %s", httpResp.StatusCode, r.URL) + return resp, fmt.Errorf("status code received %d on calling %s", httpResp.StatusCode, endpoint) } return parseResponse(httpResp) } @@ -90,26 +89,17 @@ func parseResponse(resp *http.Response) ([]byte, error) { body, err := io.ReadAll(resp.Body) resp.Body.Close() if err != nil { - return body, fmt.Errorf("failed to read airflow response: %w", err) + return body, errors.Wrap(EntityAirflow, "failed to read airflow response", err) } return body, nil } -func buildEndPoint(host, reqURL, pathParam string) string { +func buildEndPoint(host, path string) string { host = strings.Trim(host, "/") u := &url.URL{ Scheme: "http", Host: host, - } - if pathParam != "" { - u.Path = "/" + strings.ReplaceAll(reqURL, "%s", pathParam) - } else { - u.Path = "/" + reqURL - } - if reqURL == dagStatusURL { - params := url.Values{} - params.Add("limit", "99999") - u.RawQuery = params.Encode() + Path: path, } return u.String() } @@ -117,7 +107,7 @@ func buildEndPoint(host, reqURL, pathParam string) string { func getJobRuns(res DagRunListResponse, spec *cron.ScheduleSpec) ([]*scheduler.JobRunStatus, error) { var jobRunList []*scheduler.JobRunStatus if res.TotalEntries > pageLimit { - return jobRunList, errors.New("total number of entries exceed page limit") + return jobRunList, errors.InternalError(EntityAirflow, "total number of entries exceed page limit", nil) } for _, dag := range res.DagRuns { if !dag.ExternalTrigger { // only include scheduled runs diff --git a/ext/scheduler/airflow/dag/compiler.go b/ext/scheduler/airflow/dag/compiler.go index e852aa0146..7637cb2984 100644 --- a/ext/scheduler/airflow/dag/compiler.go +++ b/ext/scheduler/airflow/dag/compiler.go @@ -3,12 +3,13 @@ package dag import ( "bytes" _ "embed" + "fmt" "text/template" - "github.com/odpf/optimus/config" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/internal/errors" - "github.com/odpf/optimus/sdk/plugin" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/sdk/plugin" ) //go:embed dag.py.tmpl @@ -62,7 +63,8 @@ func (c *Compiler) Compile(jobDetails *scheduler.JobWithDetails) ([]byte, error) var buf bytes.Buffer if err = c.template.Execute(&buf, templateContext); err != nil { - return nil, errors.InternalError(EntitySchedulerAirflow, "unable to compile template for job "+jobDetails.Name.String(), err) + msg := fmt.Sprintf("unable to compile template for job %s, %s", jobDetails.Name.String(), err.Error()) + return nil, errors.InvalidArgument(EntitySchedulerAirflow, msg) } return buf.Bytes(), nil diff --git a/ext/scheduler/airflow/dag/compiler_test.go b/ext/scheduler/airflow/dag/compiler_test.go index 54d68740dd..20fc18b68e 100644 --- a/ext/scheduler/airflow/dag/compiler_test.go +++ b/ext/scheduler/airflow/dag/compiler_test.go @@ -8,12 +8,13 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/ext/scheduler/airflow/dag" - "github.com/odpf/optimus/internal/models" - "github.com/odpf/optimus/sdk/plugin" - "github.com/odpf/optimus/sdk/plugin/mock" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/ext/scheduler/airflow/dag" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/models" + "github.com/raystack/optimus/sdk/plugin" + "github.com/raystack/optimus/sdk/plugin/mock" ) //go:embed expected_dag.py @@ -23,15 +24,52 @@ func TestDagCompiler(t *testing.T) { t.Run("Compile", func(t *testing.T) { repo := setupPluginRepo() tnnt, err := tenant.NewTenant("example-proj", "billing") - assert.Nil(t, err) + assert.NoError(t, err) - t.Run("should compile basic template without any error", func(t *testing.T) { + t.Run("returns error when cannot find task", func(t *testing.T) { + emptyRepo := mockPluginRepo{plugins: []*plugin.Plugin{}} + com, err := dag.NewDagCompiler("http://optimus.example.com", emptyRepo) + assert.NoError(t, err) + + job := setupJobDetails(tnnt) + _, err = com.Compile(job) + assert.True(t, errors.IsErrorType(err, errors.ErrNotFound)) + assert.ErrorContains(t, err, "plugin not found for bq-bq") + }) + t.Run("returns error when cannot find hook", func(t *testing.T) { + com, err := dag.NewDagCompiler("http://optimus.example.com", repo) + assert.NoError(t, err) + + job := setupJobDetails(tnnt) + job.Job.Hooks = append(job.Job.Hooks, &scheduler.Hook{Name: "invalid"}) + _, err = com.Compile(job) + assert.True(t, errors.IsErrorType(err, errors.ErrNotFound)) + assert.ErrorContains(t, err, "hook not found for name invalid") + }) + + t.Run("returns error when sla duration is invalid", func(t *testing.T) { + com, err := dag.NewDagCompiler("http://optimus.example.com", repo) + assert.NoError(t, err) + + job := setupJobDetails(tnnt) + job.Alerts = append(job.Alerts, scheduler.Alert{ + On: scheduler.EventCategorySLAMiss, + }, + scheduler.Alert{ + On: scheduler.EventCategorySLAMiss, + Config: map[string]string{"duration": "2"}, + }) + _, err = com.Compile(job) + assert.ErrorContains(t, err, "failed to parse sla_miss duration 2") + }) + + t.Run("compiles basic template without any error", func(t *testing.T) { com, err := dag.NewDagCompiler("http://optimus.example.com", repo) - assert.Nil(t, err) + assert.NoError(t, err) job := setupJobDetails(tnnt) compiledDag, err := com.Compile(job) - assert.Nil(t, err) + assert.NoError(t, err) assert.Equal(t, string(compiledTemplate), string(compiledDag)) }) }) @@ -47,8 +85,7 @@ func setupJobDetails(tnnt tenant.Tenant) *scheduler.JobWithDetails { StartDate: time.Date(2022, 11, 10, 5, 2, 0, 0, time.UTC), EndDate: &end, Interval: "0 2 * * 0", - DependsOnPast: false, - CatchUp: true, + DependsOnPast: true, } retry := scheduler.Retry{ @@ -94,7 +131,6 @@ func setupJobDetails(tnnt tenant.Tenant) *scheduler.JobWithDetails { Memory: "2G", }, }, - Scheduler: map[string]string{"pool": "billing"}, } tnnt1, _ := tenant.NewTenant("project", "namespace") diff --git a/ext/scheduler/airflow/dag/dag.py.tmpl b/ext/scheduler/airflow/dag/dag.py.tmpl index b922e84406..6bb46e6156 100644 --- a/ext/scheduler/airflow/dag/dag.py.tmpl +++ b/ext/scheduler/airflow/dag/dag.py.tmpl @@ -1,12 +1,12 @@ -{{- /*gotype: github.com/odpf/optimus/ext/scheduler/airflow/dag.TemplateContext */ -}} +{{- /*gotype: github.com/raystack/optimus/ext/scheduler/airflow/dag.TemplateContext */ -}} # Code generated by optimus {{.Version}}. DO NOT EDIT. from datetime import datetime, timedelta -# imoprt Dag level callbacks +# import Dag level callbacks from __lib import job_success_event, job_failure_event -# imoprt operator level callbacks +# import operator level callbacks from __lib import operator_start_event, operator_success_event, operator_retry_event, operator_failure_event from __lib import optimus_sla_miss_notify, SuperKubernetesPodOperator, SuperExternalTaskSensor @@ -22,6 +22,10 @@ SENSOR_DEFAULT_TIMEOUT_IN_SECS = int(Variable.get("sensor_timeout_in_secs", defa DAG_RETRIES = int(Variable.get("dag_retries", default_var=3)) DAG_RETRY_DELAY = int(Variable.get("dag_retry_delay_in_secs", default_var=5 * 60)) DAGRUN_TIMEOUT_IN_SECS = int(Variable.get("dagrun_timeout_in_secs", default_var=3 * 24 * 60 * 60)) +STARTUP_TIMEOUT_IN_SECS = int(Variable.get("startup_timeout_in_secs", default_var=2 * 60)) +POOL_SENSOR = Variable.get("sensor_pool", default_var="default_pool") +POOL_TASK = Variable.get("task_pool", default_var="default_pool") +POOL_HOOK = Variable.get("hook_pool", default_var="default_pool") default_args = { "params": { @@ -30,16 +34,14 @@ default_args = { "job_name": {{.JobDetails.Name.String | quote}}, "optimus_hostname": {{.Hostname | quote}} }, - {{- if ne .RuntimeConfig.Airflow.Pool "" }} - "pool": "{{ .RuntimeConfig.Airflow.Pool }}", - {{- end }} {{- if ne .RuntimeConfig.Airflow.Queue "" }} "queue": "{{ .RuntimeConfig.Airflow.Queue }}", {{- end }} "owner": {{.JobDetails.JobMetadata.Owner | quote}}, - "depends_on_past": False, + "depends_on_past": {{ if .JobDetails.Schedule.DependsOnPast }}True{{- else -}}False{{- end -}}, "retries": {{ if gt .JobDetails.Retry.Count 0 -}} {{.JobDetails.Retry.Count}} {{- else -}} DAG_RETRIES {{- end}}, "retry_delay": {{ if gt .JobDetails.Retry.Delay 0 -}} timedelta(seconds={{.JobDetails.Retry.Delay}}) {{- else -}} timedelta(seconds=DAG_RETRY_DELAY) {{- end}}, + "startup_timeout_seconds": STARTUP_TIMEOUT_IN_SECS, "retry_exponential_backoff": {{if .JobDetails.Retry.ExponentialBackoff -}}True{{- else -}}False{{- end -}}, "priority_weight": {{.Priority}}, "start_date": datetime.strptime({{ .JobDetails.Schedule.StartDate.Format "2006-01-02T15:04:05" | quote }}, "%Y-%m-%dT%H:%M:%S"), @@ -47,6 +49,9 @@ default_args = { "end_date": datetime.strptime({{ .JobDetails.Schedule.EndDate.Format "2006-01-02T15:04:05" | quote}}, "%Y-%m-%dT%H:%M:%S"), {{- end}} "weight_rule": WeightRule.ABSOLUTE, + {{- if gt .SLAMissDuration 0 }} + "sla": timedelta(seconds={{ .SLAMissDuration }}), + {{- end }} "on_execute_callback": operator_start_event, "on_success_callback": operator_success_event, "on_retry_callback" : operator_retry_event, @@ -60,10 +65,10 @@ dag = DAG( dag_id={{.JobDetails.Name.String | quote}}, default_args=default_args, schedule_interval={{ if eq .JobDetails.Schedule.Interval "" }}None{{- else -}} {{ .JobDetails.Schedule.Interval | quote}}{{end}}, - catchup={{ if .JobDetails.Schedule.CatchUp -}}True{{- else -}}False{{- end }}, + catchup=False, dagrun_timeout=timedelta(seconds=DAGRUN_TIMEOUT_IN_SECS), tags=[ - {{- range $key, $value := $.JobDetails.JobMetadata.Labels}} + {{- range $i, $value := $.JobDetails.GetUniqueLabelValues}} "{{ $value }}", {{- end}} ], @@ -99,7 +104,7 @@ resources = k8s.V1ResourceRequirements( JOB_DIR = "/data" IMAGE_PULL_POLICY = "IfNotPresent" -INIT_CONTAINER_IMAGE = "odpf/optimus:{{.Version}}" +INIT_CONTAINER_IMAGE = "raystack/optimus:{{.Version}}" INIT_CONTAINER_ENTRYPOINT = "/opt/entrypoint_init_container.sh" def get_entrypoint_cmd(plugin_entrypoint_script): @@ -161,9 +166,6 @@ init_container = k8s.V1Container( is_delete_operator_pod=True, do_xcom_push=False, env_vars=executor_env_vars, - {{- if gt .SLAMissDuration 0 }} - sla=timedelta(seconds={{ .SLAMissDuration }}), - {{- end }} {{- if .RuntimeConfig.Resource }} resources=resources, {{- end }} @@ -171,6 +173,7 @@ init_container = k8s.V1Container( volume_mounts=asset_volume_mounts, volumes=[volume], init_containers=[init_container], + pool={{ if eq .RuntimeConfig.Airflow.Pool "" }}POOL_TASK{{- else -}} {{ .RuntimeConfig.Airflow.Pool | quote}}{{end}} ) # hooks loop start @@ -200,6 +203,7 @@ hook_{{$hookName}} = SuperKubernetesPodOperator( get_logs=True, dag=dag, in_cluster=True, + depends_on_past=False, is_delete_operator_pod=True, do_xcom_push=False, env_vars=executor_env_vars, @@ -213,6 +217,7 @@ hook_{{$hookName}} = SuperKubernetesPodOperator( volume_mounts=asset_volume_mounts, volumes=[volume], init_containers=[init_container_{{$hookName}}], + pool={{ if eq $.RuntimeConfig.Airflow.Pool "" }}POOL_HOOK{{- else -}} {{ $.RuntimeConfig.Airflow.Pool | quote}}{{end}} ) {{- end }} # hooks loop ends @@ -232,8 +237,10 @@ wait_{{ $dependencyName }} = SuperExternalTaskSensor( window_version=int("{{ $baseWindow.GetVersion }}"), poke_interval=SENSOR_DEFAULT_POKE_INTERVAL_IN_SECS, timeout=SENSOR_DEFAULT_TIMEOUT_IN_SECS, - task_id="wait_{{$upstream.JobName | trunc 200}}-{{$upstream.TaskName}}", - dag=dag + task_id="wait_{{$upstream.JobName}}-{{$upstream.TaskName}}", + depends_on_past=False, + dag=dag, + pool={{ if eq $.RuntimeConfig.Airflow.Pool "" }}POOL_SENSOR{{- else -}} {{ $.RuntimeConfig.Airflow.Pool | quote}}{{end}} ) {{ end}} @@ -247,8 +254,10 @@ wait_{{$httpUpstream.Name}} = ExternalHttpSensor( request_params=request_params_dict_{{$httpUpstream.Name}}, poke_interval=SENSOR_DEFAULT_POKE_INTERVAL_IN_SECS, timeout=SENSOR_DEFAULT_TIMEOUT_IN_SECS, - task_id='wait_{{$httpUpstream.Name | trunc 200}}', - dag=dag + task_id='wait_{{$httpUpstream.Name}}', + depends_on_past=False, + dag=dag, + pool={{ if eq $.RuntimeConfig.Airflow.Pool "" }}POOL_SENSOR{{- else -}} {{ $.RuntimeConfig.Airflow.Pool | quote}}{{end}} ) {{- end -}} diff --git a/ext/scheduler/airflow/dag/expected_dag.py b/ext/scheduler/airflow/dag/expected_dag.py index c54ca5f96e..87a61094ab 100644 --- a/ext/scheduler/airflow/dag/expected_dag.py +++ b/ext/scheduler/airflow/dag/expected_dag.py @@ -2,10 +2,10 @@ from datetime import datetime, timedelta -# imoprt Dag level callbacks +# import Dag level callbacks from __lib import job_success_event, job_failure_event -# imoprt operator level callbacks +# import operator level callbacks from __lib import operator_start_event, operator_success_event, operator_retry_event, operator_failure_event from __lib import optimus_sla_miss_notify, SuperKubernetesPodOperator, SuperExternalTaskSensor @@ -21,6 +21,10 @@ DAG_RETRIES = int(Variable.get("dag_retries", default_var=3)) DAG_RETRY_DELAY = int(Variable.get("dag_retry_delay_in_secs", default_var=5 * 60)) DAGRUN_TIMEOUT_IN_SECS = int(Variable.get("dagrun_timeout_in_secs", default_var=3 * 24 * 60 * 60)) +STARTUP_TIMEOUT_IN_SECS = int(Variable.get("startup_timeout_in_secs", default_var=2 * 60)) +POOL_SENSOR = Variable.get("sensor_pool", default_var="default_pool") +POOL_TASK = Variable.get("task_pool", default_var="default_pool") +POOL_HOOK = Variable.get("hook_pool", default_var="default_pool") default_args = { "params": { @@ -29,16 +33,17 @@ "job_name": "infra.billing.weekly-status-reports", "optimus_hostname": "http://optimus.example.com" }, - "pool": "billing", "owner": "infra-team@example.com", - "depends_on_past": False, + "depends_on_past": True, "retries": 2, "retry_delay": timedelta(seconds=100), + "startup_timeout_seconds": STARTUP_TIMEOUT_IN_SECS, "retry_exponential_backoff": True, "priority_weight": 2000, "start_date": datetime.strptime("2022-11-10T05:02:00", "%Y-%m-%dT%H:%M:%S"), "end_date": datetime.strptime("2022-11-10T10:02:00", "%Y-%m-%dT%H:%M:%S"), "weight_rule": WeightRule.ABSOLUTE, + "sla": timedelta(seconds=7200), "on_execute_callback": operator_start_event, "on_success_callback": operator_success_event, "on_retry_callback" : operator_retry_event, @@ -50,7 +55,7 @@ dag_id="infra.billing.weekly-status-reports", default_args=default_args, schedule_interval="0 2 * * 0", - catchup=True, + catchup=False, dagrun_timeout=timedelta(seconds=DAGRUN_TIMEOUT_IN_SECS), tags=[ "optimus", @@ -69,7 +74,7 @@ JOB_DIR = "/data" IMAGE_PULL_POLICY = "IfNotPresent" -INIT_CONTAINER_IMAGE = "odpf/optimus:dev" +INIT_CONTAINER_IMAGE = "raystack/optimus:dev" INIT_CONTAINER_ENTRYPOINT = "/opt/entrypoint_init_container.sh" def get_entrypoint_cmd(plugin_entrypoint_script): @@ -125,17 +130,17 @@ def get_entrypoint_cmd(plugin_entrypoint_script): task_id="bq-bq", get_logs=True, dag=dag, - depends_on_past=False, + depends_on_past=True, in_cluster=True, is_delete_operator_pod=True, do_xcom_push=False, env_vars=executor_env_vars, - sla=timedelta(seconds=7200), resources=resources, reattach_on_restart=True, volume_mounts=asset_volume_mounts, volumes=[volume], init_containers=[init_container], + pool=POOL_TASK ) # hooks loop start @@ -163,6 +168,7 @@ def get_entrypoint_cmd(plugin_entrypoint_script): get_logs=True, dag=dag, in_cluster=True, + depends_on_past=False, is_delete_operator_pod=True, do_xcom_push=False, env_vars=executor_env_vars, @@ -171,6 +177,7 @@ def get_entrypoint_cmd(plugin_entrypoint_script): volume_mounts=asset_volume_mounts, volumes=[volume], init_containers=[init_container_transporter], + pool=POOL_HOOK ) init_container_predator = k8s.V1Container( name="init-container", @@ -196,6 +203,7 @@ def get_entrypoint_cmd(plugin_entrypoint_script): get_logs=True, dag=dag, in_cluster=True, + depends_on_past=False, is_delete_operator_pod=True, do_xcom_push=False, env_vars=executor_env_vars, @@ -204,6 +212,7 @@ def get_entrypoint_cmd(plugin_entrypoint_script): volume_mounts=asset_volume_mounts, volumes=[volume], init_containers=[init_container_predator], + pool=POOL_HOOK ) init_container_failureHook = k8s.V1Container( name="init-container", @@ -229,6 +238,7 @@ def get_entrypoint_cmd(plugin_entrypoint_script): get_logs=True, dag=dag, in_cluster=True, + depends_on_past=False, is_delete_operator_pod=True, do_xcom_push=False, env_vars=executor_env_vars, @@ -238,6 +248,7 @@ def get_entrypoint_cmd(plugin_entrypoint_script): volume_mounts=asset_volume_mounts, volumes=[volume], init_containers=[init_container_failureHook], + pool=POOL_HOOK ) # hooks loop ends @@ -254,7 +265,9 @@ def get_entrypoint_cmd(plugin_entrypoint_script): poke_interval=SENSOR_DEFAULT_POKE_INTERVAL_IN_SECS, timeout=SENSOR_DEFAULT_TIMEOUT_IN_SECS, task_id="wait_foo-intra-dep-job-bq", - dag=dag + depends_on_past=False, + dag=dag, + pool=POOL_SENSOR ) wait_foo__dash__inter__dash__dep__dash__job = SuperExternalTaskSensor( @@ -268,7 +281,9 @@ def get_entrypoint_cmd(plugin_entrypoint_script): poke_interval=SENSOR_DEFAULT_POKE_INTERVAL_IN_SECS, timeout=SENSOR_DEFAULT_TIMEOUT_IN_SECS, task_id="wait_foo-inter-dep-job-bq-bq", - dag=dag + depends_on_past=False, + dag=dag, + pool=POOL_SENSOR ) wait_foo__dash__external__dash__optimus__dash__dep__dash__job = SuperExternalTaskSensor( @@ -282,7 +297,9 @@ def get_entrypoint_cmd(plugin_entrypoint_script): poke_interval=SENSOR_DEFAULT_POKE_INTERVAL_IN_SECS, timeout=SENSOR_DEFAULT_TIMEOUT_IN_SECS, task_id="wait_foo-external-optimus-dep-job-bq-bq", - dag=dag + depends_on_past=False, + dag=dag, + pool=POOL_SENSOR ) # arrange inter task dependencies #################################### diff --git a/ext/scheduler/airflow/dag/models.go b/ext/scheduler/airflow/dag/models.go index f692810ed1..75fc1ac7c7 100644 --- a/ext/scheduler/airflow/dag/models.go +++ b/ext/scheduler/airflow/dag/models.go @@ -3,10 +3,10 @@ package dag import ( "time" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" - "github.com/odpf/optimus/sdk/plugin" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/sdk/plugin" ) const ( diff --git a/ext/scheduler/airflow/dag/template_func_test.go b/ext/scheduler/airflow/dag/template_func_test.go index d580280b25..a4f6a2614c 100644 --- a/ext/scheduler/airflow/dag/template_func_test.go +++ b/ext/scheduler/airflow/dag/template_func_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/ext/scheduler/airflow/dag" + "github.com/raystack/optimus/ext/scheduler/airflow/dag" ) func TestTemplateFuncMap(t *testing.T) { diff --git a/ext/scheduler/airflow/dag/upstream.go b/ext/scheduler/airflow/dag/upstream.go index 517dc0cd92..82bd2c1dbd 100644 --- a/ext/scheduler/airflow/dag/upstream.go +++ b/ext/scheduler/airflow/dag/upstream.go @@ -1,8 +1,8 @@ package dag import ( - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/tenant" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/tenant" ) type Upstreams struct { diff --git a/ext/store/bigquery/backup.go b/ext/store/bigquery/backup.go index fcd02602a3..25bd2f0f5f 100644 --- a/ext/store/bigquery/backup.go +++ b/ext/store/bigquery/backup.go @@ -5,9 +5,9 @@ import ( "fmt" "time" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" ) const ( diff --git a/ext/store/bigquery/backup_test.go b/ext/store/bigquery/backup_test.go index 7c2a468080..0b5e5713f5 100644 --- a/ext/store/bigquery/backup_test.go +++ b/ext/store/bigquery/backup_test.go @@ -8,10 +8,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/ext/store/bigquery" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/ext/store/bigquery" + "github.com/raystack/optimus/internal/errors" ) func TestBigqueryBackup(t *testing.T) { diff --git a/ext/store/bigquery/batch.go b/ext/store/bigquery/batch.go index 346ff6f9f9..730c36e4a0 100644 --- a/ext/store/bigquery/batch.go +++ b/ext/store/bigquery/batch.go @@ -5,9 +5,9 @@ import ( "github.com/kushsharma/parallel" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" ) type Batch struct { @@ -183,5 +183,5 @@ func BatchesFrom(resources []*resource.Resource, provider ClientProvider) (map[s mapping[dataset.FullName()] = batch } - return mapping, errors.MultiToError(me) + return mapping, me.ToErr() } diff --git a/ext/store/bigquery/batch_test.go b/ext/store/bigquery/batch_test.go index bccc1bec8b..4631509cdb 100644 --- a/ext/store/bigquery/batch_test.go +++ b/ext/store/bigquery/batch_test.go @@ -8,10 +8,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/ext/store/bigquery" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/ext/store/bigquery" + "github.com/raystack/optimus/internal/errors" ) func TestBatches(t *testing.T) { diff --git a/ext/store/bigquery/bigquery.go b/ext/store/bigquery/bigquery.go index c470930772..f3fc3dd621 100644 --- a/ext/store/bigquery/bigquery.go +++ b/ext/store/bigquery/bigquery.go @@ -9,9 +9,9 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/trace" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" ) const ( @@ -180,7 +180,7 @@ func (s Store) BatchUpdate(ctx context.Context, resources []*resource.Resource) me.Append(state.Err) } - return errors.MultiToError(me) + return me.ToErr() } func (Store) Validate(r *resource.Resource) error { diff --git a/ext/store/bigquery/bigquery_test.go b/ext/store/bigquery/bigquery_test.go index fb99bc3a8e..199b946942 100644 --- a/ext/store/bigquery/bigquery_test.go +++ b/ext/store/bigquery/bigquery_test.go @@ -10,9 +10,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/ext/store/bigquery" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/ext/store/bigquery" ) func TestBigqueryStore(t *testing.T) { diff --git a/ext/store/bigquery/client.go b/ext/store/bigquery/client.go index 4076cc8298..154f8c2b97 100644 --- a/ext/store/bigquery/client.go +++ b/ext/store/bigquery/client.go @@ -7,7 +7,7 @@ import ( "golang.org/x/oauth2/google" "google.golang.org/api/option" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/internal/errors" ) type BqClientProvider struct{} diff --git a/ext/store/bigquery/client_test.go b/ext/store/bigquery/client_test.go index bce1a1afc4..f5d7badb8c 100644 --- a/ext/store/bigquery/client_test.go +++ b/ext/store/bigquery/client_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/ext/store/bigquery" + "github.com/raystack/optimus/ext/store/bigquery" ) func TestBqClient(t *testing.T) { @@ -118,7 +118,7 @@ func TestClientProvider(t *testing.T) { _, err := provider.Get(ctx, "") assert.NotNil(t, err) - assert.EqualError(t, err, "internal error for entity BigqueryStore: failed to read account") + assert.ErrorContains(t, err, "internal error for entity BigqueryStore: failed to read account") }) t.Run("creates a new client with json", func(t *testing.T) { diff --git a/ext/store/bigquery/copier.go b/ext/store/bigquery/copier.go index 08e6b1150a..0f53c187ca 100644 --- a/ext/store/bigquery/copier.go +++ b/ext/store/bigquery/copier.go @@ -5,7 +5,7 @@ import ( "cloud.google.com/go/bigquery" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/internal/errors" ) type BQCopier interface { diff --git a/ext/store/bigquery/copier_test.go b/ext/store/bigquery/copier_test.go index 86f22faf20..5b997875ae 100644 --- a/ext/store/bigquery/copier_test.go +++ b/ext/store/bigquery/copier_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/ext/store/bigquery" + "github.com/raystack/optimus/ext/store/bigquery" ) func TestTableCopier(t *testing.T) { @@ -25,7 +25,7 @@ func TestTableCopier(t *testing.T) { _, err := copierHandle.Run(ctx) assert.Error(t, err) - assert.EqualError(t, err, "internal error for entity BigqueryStore: not able to create copy job") + assert.ErrorContains(t, err, "internal error for entity BigqueryStore: not able to create copy job") }) t.Run("return copy job when successful", func(t *testing.T) { bqCopier := new(mockBQCopier) diff --git a/ext/store/bigquery/dataset.go b/ext/store/bigquery/dataset.go index 3d8bbac285..4f55dc89e8 100644 --- a/ext/store/bigquery/dataset.go +++ b/ext/store/bigquery/dataset.go @@ -9,8 +9,8 @@ import ( "cloud.google.com/go/bigquery" "google.golang.org/api/googleapi" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/internal/errors" ) const ( diff --git a/ext/store/bigquery/dataset_spec.go b/ext/store/bigquery/dataset_spec.go index 81c15a1241..eec2a082a6 100644 --- a/ext/store/bigquery/dataset_spec.go +++ b/ext/store/bigquery/dataset_spec.go @@ -6,8 +6,8 @@ import ( "github.com/mitchellh/mapstructure" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/internal/errors" ) const ( diff --git a/ext/store/bigquery/dataset_spec_test.go b/ext/store/bigquery/dataset_spec_test.go index 4ba5499a81..9d156e783e 100644 --- a/ext/store/bigquery/dataset_spec_test.go +++ b/ext/store/bigquery/dataset_spec_test.go @@ -5,9 +5,9 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/ext/store/bigquery" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/ext/store/bigquery" ) func TestDatasetDetails(t *testing.T) { diff --git a/ext/store/bigquery/dataset_test.go b/ext/store/bigquery/dataset_test.go index 495f55b5d8..1741c86d35 100644 --- a/ext/store/bigquery/dataset_test.go +++ b/ext/store/bigquery/dataset_test.go @@ -10,9 +10,9 @@ import ( "github.com/stretchr/testify/mock" "google.golang.org/api/googleapi" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/ext/store/bigquery" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/ext/store/bigquery" ) func TestDatasetHandle(t *testing.T) { diff --git a/ext/store/bigquery/external_table.go b/ext/store/bigquery/external_table.go index a0de750e04..4cbf47e986 100644 --- a/ext/store/bigquery/external_table.go +++ b/ext/store/bigquery/external_table.go @@ -8,12 +8,15 @@ import ( "cloud.google.com/go/bigquery" "google.golang.org/api/googleapi" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/internal/errors" ) const ( expirationTimeKey = "expiration_time" + + skipLeadingRowsKey = "skip_leading_rows" + rangeKey = "range" ) type ExternalTableHandle struct { @@ -120,18 +123,14 @@ func bqExternalDataConfigTo(es *ExternalSource, schema Schema) (*bigquery.Extern func bqGoogleSheetsOptionsTo(m map[string]any) *bigquery.GoogleSheetsOptions { var skipLeadingRows int64 - var sheetRange string - if val, ok := m["skip_leading_rows"]; ok { - if rows, ok := val.(int); ok { - skipLeadingRows = int64(rows) - } - } - if val, ok := m["range"]; ok { - if ran, ok := val.(string); ok { - sheetRange = ran - } + // grpc structpb.Struct cast numbers to float64 + rows := ConfigAs[float64](m, skipLeadingRowsKey) + if rows > 0 { + skipLeadingRows = int64(rows) } + + sheetRange := ConfigAs[string](m, rangeKey) return &bigquery.GoogleSheetsOptions{ SkipLeadingRows: skipLeadingRows, Range: sheetRange, diff --git a/ext/store/bigquery/external_table_spec.go b/ext/store/bigquery/external_table_spec.go index fef774d6cb..7ceac4a0f7 100644 --- a/ext/store/bigquery/external_table_spec.go +++ b/ext/store/bigquery/external_table_spec.go @@ -1,8 +1,8 @@ package bigquery import ( - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/internal/errors" ) const ( diff --git a/ext/store/bigquery/external_table_spec_test.go b/ext/store/bigquery/external_table_spec_test.go index 6fb750e0ff..bcef6489c6 100644 --- a/ext/store/bigquery/external_table_spec_test.go +++ b/ext/store/bigquery/external_table_spec_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/ext/store/bigquery" + "github.com/raystack/optimus/ext/store/bigquery" ) func TestRelationalExternalTable(t *testing.T) { diff --git a/ext/store/bigquery/external_table_test.go b/ext/store/bigquery/external_table_test.go index 206e1348bf..db12b1ee95 100644 --- a/ext/store/bigquery/external_table_test.go +++ b/ext/store/bigquery/external_table_test.go @@ -10,9 +10,9 @@ import ( "github.com/stretchr/testify/mock" "google.golang.org/api/googleapi" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/ext/store/bigquery" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/ext/store/bigquery" ) var emptyUpdateOptions []bq.TableUpdateOption @@ -147,7 +147,7 @@ func TestExternalTableHandle(t *testing.T) { "uris": []string{"https://docs.google.com/sheet"}, "config": map[string]any{ "range": "kyc", - "skip_leading_rows": 2, + "skip_leading_rows": float64(2), }, }, } diff --git a/ext/store/bigquery/job.go b/ext/store/bigquery/job.go index e1710b9afe..cde1e43270 100644 --- a/ext/store/bigquery/job.go +++ b/ext/store/bigquery/job.go @@ -5,7 +5,7 @@ import ( "cloud.google.com/go/bigquery" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/internal/errors" ) type BQJob interface { diff --git a/ext/store/bigquery/job_test.go b/ext/store/bigquery/job_test.go index 2ca81216e5..b817e8307c 100644 --- a/ext/store/bigquery/job_test.go +++ b/ext/store/bigquery/job_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/ext/store/bigquery" + "github.com/raystack/optimus/ext/store/bigquery" ) func TestJob(t *testing.T) { @@ -24,7 +24,7 @@ func TestJob(t *testing.T) { err := copyJob.Wait(ctx) assert.Error(t, err) - assert.EqualError(t, err, "internal error for entity BigqueryStore: error while wait for bq job") + assert.ErrorContains(t, err, "internal error for entity BigqueryStore: error while wait for bq job") }) t.Run("return no error when successful", func(t *testing.T) { bqJob := new(mockBQJob) diff --git a/ext/store/bigquery/schema.go b/ext/store/bigquery/schema.go index c8ec9c6ead..10c47b84c6 100644 --- a/ext/store/bigquery/schema.go +++ b/ext/store/bigquery/schema.go @@ -3,7 +3,7 @@ package bigquery import ( "strings" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/internal/errors" ) const ( diff --git a/ext/store/bigquery/schema_test.go b/ext/store/bigquery/schema_test.go index f392aa64ec..31372947d8 100644 --- a/ext/store/bigquery/schema_test.go +++ b/ext/store/bigquery/schema_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/ext/store/bigquery" + "github.com/raystack/optimus/ext/store/bigquery" ) func TestFieldValidate(t *testing.T) { diff --git a/ext/store/bigquery/table.go b/ext/store/bigquery/table.go index 30fdf47c02..c1135f8735 100644 --- a/ext/store/bigquery/table.go +++ b/ext/store/bigquery/table.go @@ -9,8 +9,8 @@ import ( "cloud.google.com/go/bigquery" "google.golang.org/api/googleapi" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/internal/errors" ) type BqTable interface { diff --git a/ext/store/bigquery/table_spec.go b/ext/store/bigquery/table_spec.go index 0e455378d1..581588dc2a 100644 --- a/ext/store/bigquery/table_spec.go +++ b/ext/store/bigquery/table_spec.go @@ -3,8 +3,8 @@ package bigquery import ( "strings" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/internal/errors" ) const ( diff --git a/ext/store/bigquery/table_spec_test.go b/ext/store/bigquery/table_spec_test.go index 185b797924..5afa52f17f 100644 --- a/ext/store/bigquery/table_spec_test.go +++ b/ext/store/bigquery/table_spec_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/ext/store/bigquery" + "github.com/raystack/optimus/ext/store/bigquery" ) func TestRelationalTable(t *testing.T) { diff --git a/ext/store/bigquery/table_test.go b/ext/store/bigquery/table_test.go index 530ef72d86..ae5486d4ed 100644 --- a/ext/store/bigquery/table_test.go +++ b/ext/store/bigquery/table_test.go @@ -11,9 +11,9 @@ import ( "github.com/stretchr/testify/mock" "google.golang.org/api/googleapi" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/ext/store/bigquery" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/ext/store/bigquery" ) func TestTableHandle(t *testing.T) { diff --git a/ext/store/bigquery/view.go b/ext/store/bigquery/view.go index 7eb02f83f9..56a3575059 100644 --- a/ext/store/bigquery/view.go +++ b/ext/store/bigquery/view.go @@ -9,8 +9,8 @@ import ( "cloud.google.com/go/bigquery" "google.golang.org/api/googleapi" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/internal/errors" ) type ViewHandle struct { diff --git a/ext/store/bigquery/view_spec.go b/ext/store/bigquery/view_spec.go index 079cdc12cd..d32e219630 100644 --- a/ext/store/bigquery/view_spec.go +++ b/ext/store/bigquery/view_spec.go @@ -1,8 +1,8 @@ package bigquery import ( - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/internal/errors" ) const ( diff --git a/ext/store/bigquery/view_spec_test.go b/ext/store/bigquery/view_spec_test.go index 20937ee8f2..ba743ff2d1 100644 --- a/ext/store/bigquery/view_spec_test.go +++ b/ext/store/bigquery/view_spec_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/ext/store/bigquery" + "github.com/raystack/optimus/ext/store/bigquery" ) func TestRelationalView(t *testing.T) { diff --git a/ext/store/bigquery/view_test.go b/ext/store/bigquery/view_test.go index 71a3d4b451..229a453c7d 100644 --- a/ext/store/bigquery/view_test.go +++ b/ext/store/bigquery/view_test.go @@ -11,9 +11,9 @@ import ( "github.com/stretchr/testify/mock" "google.golang.org/api/googleapi" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/ext/store/bigquery" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/ext/store/bigquery" ) func TestViewHandle(t *testing.T) { diff --git a/ext/transport/kafka/writer.go b/ext/transport/kafka/writer.go new file mode 100644 index 0000000000..ce47148656 --- /dev/null +++ b/ext/transport/kafka/writer.go @@ -0,0 +1,60 @@ +package kafka + +import ( + "context" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/raystack/salt/log" + "github.com/segmentio/kafka-go" +) + +const ( + writeTimeout = time.Second * 3 +) + +var kafkaQueueCounter = promauto.NewCounter(prometheus.CounterOpts{ + Name: "publisher_kafka_events_queued_total", + Help: "Number of events queued to be published to kafka topic", +}) + +type Writer struct { + kafkaWriter *kafka.Writer +} + +func NewWriter(kafkaBrokerUrls []string, topic string, logger log.Logger) *Writer { + writer := &kafka.Writer{ + Addr: kafka.TCP(kafkaBrokerUrls...), + Topic: topic, + AllowAutoTopicCreation: true, + Balancer: &kafka.LeastBytes{}, + RequiredAcks: kafka.RequireOne, + MaxAttempts: 1, + WriteTimeout: writeTimeout, + Logger: kafka.LoggerFunc(logger.Info), + ErrorLogger: kafka.LoggerFunc(logger.Error), + } + + return &Writer{kafkaWriter: writer} +} + +func (w *Writer) Close() error { + return w.kafkaWriter.Close() +} + +func (w *Writer) Write(messages [][]byte) error { + kafkaMessages := make([]kafka.Message, len(messages)) + for i, m := range messages { + kafkaMessages[i] = kafka.Message{ + Value: m, + } + } + + err := w.kafkaWriter.WriteMessages(context.Background(), kafkaMessages...) + if err == nil { + kafkaQueueCounter.Add(float64(len(messages))) + return nil + } + return err +} diff --git a/go.mod b/go.mod index c243883246..221131bbd0 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,8 @@ -module github.com/odpf/optimus +module github.com/raystack/optimus go 1.19 -replace github.com/odpf/optimus/sdk => ./sdk +replace github.com/raystack/optimus/sdk => ./sdk require ( cloud.google.com/go/bigquery v1.44.0 @@ -13,14 +13,13 @@ require ( github.com/charmbracelet/bubbles v0.13.0 github.com/charmbracelet/bubbletea v0.22.1 github.com/dustinkirkland/golang-petname v0.0.0-20191129215211-8e5a1ed0cff0 - github.com/emirpasic/gods v1.12.0 github.com/fatih/color v1.7.0 github.com/go-ozzo/ozzo-validation/v4 v4.3.0 github.com/golang-migrate/migrate/v4 v4.15.2 github.com/google/uuid v1.3.0 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 - github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0 + github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 github.com/gtank/cryptopasta v0.0.0-20170601214702-1f550f6f2f69 github.com/hashicorp/go-getter v1.6.2 github.com/hashicorp/go-hclog v0.14.1 @@ -30,19 +29,21 @@ require ( github.com/lib/pq v1.10.4 github.com/mattn/go-isatty v0.0.16 github.com/mitchellh/mapstructure v1.4.3 - github.com/odpf/optimus/sdk v0.0.0-20230104081553-fa05355b2dda - github.com/odpf/salt v0.0.0-20220614042821-c5613a78b4d6 github.com/olekukonko/tablewriter v0.0.5 github.com/prometheus/client_golang v1.11.0 + github.com/raystack/optimus/sdk v0.0.0-20230313071811-2d68a9c815bf + github.com/raystack/salt v0.3.2 github.com/robfig/cron/v3 v3.0.1 github.com/schollz/progressbar/v3 v3.8.5 + github.com/segmentio/kafka-go v0.4.39 github.com/sirupsen/logrus v1.8.1 github.com/slack-go/slack v0.9.1 - github.com/spf13/afero v1.8.2 + github.com/spf13/afero v1.9.2 github.com/spf13/cobra v1.2.1 github.com/spf13/viper v1.8.1 github.com/stretchr/testify v1.8.1 github.com/xlab/treeprint v1.1.0 + github.com/zalando/go-keyring v0.2.3 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.32.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.32.0 go.opentelemetry.io/otel v1.7.0 @@ -68,6 +69,7 @@ require ( cloud.google.com/go/iam v0.7.0 // indirect cloud.google.com/go/storage v1.27.0 // indirect github.com/alecthomas/chroma v0.8.2 // indirect + github.com/alessio/shellescape v1.4.1 // indirect github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect github.com/atotto/clipboard v0.1.4 // indirect github.com/aws/aws-sdk-go v1.43.31 // indirect @@ -77,7 +79,10 @@ require ( github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/charmbracelet/glamour v0.3.0 // indirect github.com/charmbracelet/lipgloss v0.5.0 // indirect + github.com/cli/safeexec v1.0.0 // indirect github.com/containerd/console v1.0.3 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect + github.com/danieljoos/wincred v1.2.0 // indirect github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dlclark/regexp2 v1.2.0 // indirect @@ -85,10 +90,12 @@ require ( github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/go-querystring v1.1.0 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/wire v0.5.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect github.com/googleapis/gax-go/v2 v2.7.0 // indirect @@ -109,7 +116,7 @@ require ( github.com/jhump/protoreflect v1.9.1-0.20210817181203-db1a327a393e // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect - github.com/klauspost/compress v1.15.1 // indirect + github.com/klauspost/compress v1.15.9 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/magiconair/properties v1.8.5 // indirect github.com/mattn/go-colorable v0.1.6 // indirect @@ -126,8 +133,9 @@ require ( github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739 // indirect - github.com/oklog/run v1.0.0 // indirect + github.com/oklog/run v1.1.0 // indirect github.com/pelletier/go-toml v1.9.3 // indirect + github.com/pierrec/lz4/v4 v4.1.15 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect @@ -135,13 +143,15 @@ require ( github.com/prometheus/procfs v0.7.3 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect + github.com/russross/blackfriday/v2 v2.0.1 // indirect + github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect github.com/spf13/cast v1.3.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.0 // indirect github.com/subosito/gotenv v1.2.0 // indirect github.com/ulikunitz/xz v0.5.8 // indirect - github.com/yuin/goldmark v1.4.1 // indirect + github.com/yuin/goldmark v1.4.13 // indirect github.com/yuin/goldmark-emoji v1.0.1 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/otel/metric v0.30.0 // indirect @@ -151,7 +161,7 @@ require ( go.uber.org/zap v1.21.0 // indirect golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect + golang.org/x/sys v0.8.0 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.4.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect diff --git a/go.sum b/go.sum index 3904cd5e1f..4b9d556d92 100644 --- a/go.sum +++ b/go.sum @@ -34,6 +34,7 @@ cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0c cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= cloud.google.com/go v0.105.0 h1:DNtEKRBAAzeS4KyIory52wWHuClNaXJ5x1F7xa4q+5Y= cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= @@ -48,6 +49,9 @@ cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTB cloud.google.com/go/compute v1.2.0/go.mod h1:xlogom/6gr8RJGBe7nT2eGsQYAFUbbv8dbC29qE3Xmw= cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= +cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= +cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= cloud.google.com/go/compute v1.12.1 h1:gKVJMEyqV5c/UnpzjjQbo3Rjvvqpr9B1DFSbJC4OXr0= cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= cloud.google.com/go/compute/metadata v0.2.1 h1:efOwf5ymceDhK6PKMnnrTHP4pppY5L22mle96M1yP48= @@ -81,6 +85,7 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= cloud.google.com/go/storage v1.21.0/go.mod h1:XmRlxkgPjlBONznT2dDUU/5XlpU2OjMnKuqnZI01LAA= +cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= cloud.google.com/go/storage v1.27.0 h1:YOO045NZI9RKfCj1c5A/ZtuuENUc8OAW+gHdGnDgyMQ= cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= cloud.google.com/go/trace v1.0.0/go.mod h1:4iErSByzxkyHWzzlAj63/Gmjz0NH1ASqhJguHpGcr6A= @@ -91,6 +96,7 @@ contrib.go.opencensus.io/integrations/ocsql v0.1.7/go.mod h1:8DsSdjz3F+APR+0z0Wk dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg= +github.com/AlecAivazis/survey/v2 v2.3.5/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI= github.com/AlecAivazis/survey/v2 v2.3.6 h1:NvTuVHISgTHEHeBFqt6BHOe4Ny/NwGZr7w+F8S9ziyw= github.com/AlecAivazis/survey/v2 v2.3.6/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI= github.com/Azure/azure-amqp-common-go/v3 v3.2.1/go.mod h1:O6X1iYHP7s2x7NjUKsXVhkwWrQhxrd+d8/3rRadj4CI= @@ -137,7 +143,6 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp 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/ClickHouse/clickhouse-go v1.4.3/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= -github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/GoogleCloudPlatform/cloudsql-proxy v1.29.0/go.mod h1:spvB9eLJH9dutlbPSRmHvSXXHOwGRyeXh1jVdquA2G8= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= @@ -170,6 +175,7 @@ github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb0 github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PagerDuty/go-pagerduty v1.5.1 h1:zpMQ8WwWlUahipB2q+ERVIA9D0/ti8kvsQUSagCK86g= github.com/PagerDuty/go-pagerduty v1.5.1/go.mod h1:txr8VbObXdk2RkqF+C2an4qWssdGY99fK26XYUDjh+4= @@ -193,6 +199,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= +github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= github.com/alexflint/go-filemutex v1.1.0/go.mod h1:7P4iRhttt/nUvUOrYIhcpMzv2G6CY9UnI16Z+UJqRyk= github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI= @@ -209,6 +217,9 @@ github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 h1:zV3ejI06 github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= +github.com/authzed/authzed-go v0.7.0/go.mod h1:bmjzzIQ34M0+z8NO9SLjf4oA0A9Ka9gUWVzeSbD0E7c= +github.com/authzed/grpcutil v0.0.0-20210913124023-cad23ae5a9e8/go.mod h1:HwO/KbRU3fWXEYHE96kvXnwxzi97tkXD1hfi5UaZ71Y= +github.com/authzed/grpcutil v0.0.0-20220104222419-f813f77722e5/go.mod h1:rqjY3zyK/YP7NID9+B2BdIRRkvnK+cdf9/qya/zaFZE= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= @@ -314,10 +325,12 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0Bsq github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= +github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -345,6 +358,8 @@ github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= +github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI= +github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= @@ -411,6 +426,7 @@ github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= github.com/containerd/continuity v0.2.2/go.mod h1:pWygW9u7LtS1o4N/Tn0FoCFDIXZ7rxcMX7HX1Dmibvk= +github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= @@ -480,6 +496,7 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -493,12 +510,13 @@ github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1S github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= +github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE= +github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU= github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= @@ -515,12 +533,14 @@ github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55k github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v20.10.14+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.13+incompatible h1:5s7uxnKZG+b8hYWlPYUi6x1Sjpq2MSt96d15eLZeHyw= github.com/docker/docker v20.10.13+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= @@ -543,8 +563,6 @@ github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= -github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -557,6 +575,7 @@ github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPO github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= +github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= @@ -675,6 +694,8 @@ github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6 github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= @@ -753,6 +774,7 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= @@ -792,6 +814,8 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -801,6 +825,7 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8= github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= +github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.2.0 h1:y8Yozv7SZtlU//QXbezB6QkpuE6jMD2/gfzk4AftXjs= github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -808,11 +833,14 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ= github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= +github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -841,8 +869,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0 h1:rgxjzoDmDXw5q8HONgyHhBas4to0/XWRo/gPpJhsUNQ= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0/go.mod h1:qrJPVzv9YlhsrxJc3P/Q85nr0w1lIRikTl4JlhdDH5w= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 h1:lLT7ZLSzGLI08vc9cpd+tYmNWjdKDqyr/2L+f6U12Fk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= github.com/gtank/cryptopasta v0.0.0-20170601214702-1f550f6f2f69 h1:7xsUJsB2NrdcttQPa7JLEaGzvdbk7KvfrjgHZXOQRo0= github.com/gtank/cryptopasta v0.0.0-20170601214702-1f550f6f2f69/go.mod h1:YLEMZOtU+AZ7dhN9T/IpGhXVGly2bvkJQ+zxj3WeVQo= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= @@ -916,7 +944,6 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk= github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= -github.com/jackc/pgconn v1.7.0/go.mod h1:sF/lPpNEMEOp+IYhyQGdAvrG20gWf6A1tKlr0v7JMeA= github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= @@ -934,7 +961,6 @@ github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.0.5/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.0.7/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= @@ -948,7 +974,6 @@ github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrU github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0= github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po= github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ= -github.com/jackc/pgtype v1.5.0/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig= github.com/jackc/pgtype v1.6.2/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig= github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= github.com/jackc/pgtype v1.10.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= @@ -958,7 +983,6 @@ github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQ github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA= github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o= github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg= -github.com/jackc/pgx/v4 v4.9.0/go.mod h1:MNGWmViCgqbZck9ujOOBN63gK9XVGILXWCvKLGKmnms= github.com/jackc/pgx/v4 v4.10.1/go.mod h1:QlrWebbs3kqEZPHCTGyxecvzG6tvIsYu+A5b1raylkA= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= github.com/jackc/pgx/v4 v4.15.0/go.mod h1:D/zyOyXiaM1TmVWnOM18p0xdDtdakRBa0RsVGI3U3bw= @@ -968,7 +992,6 @@ github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0f github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.2/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle/v2 v2.1.2 h1:0f7vaaXINONKTsxYDn4otOAiJanX/BMeAtY//BXqzlg= @@ -989,6 +1012,7 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/jmoiron/sqlx v1.3.1/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= +github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -1009,6 +1033,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/jzelinskie/stringz v0.0.0-20210414224931-d6a8ce844a70/go.mod h1:hHYbgxJuNLRw91CmpuFsYEOyQqpDVFg8pvEh23vy4P0= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/k0kubun/pp v2.3.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= @@ -1029,8 +1054,9 @@ github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdY github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.1 h1:y9FcTHGyrebwfP0ZZqFiaxTaiDnUrGkJkI+f583BL1A= github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -1052,6 +1078,7 @@ github.com/kushsharma/parallel v0.2.1/go.mod h1:6JCy2+DRCUfZ0VFBUg6HG8IdDTDKuVL0 github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/lib/pq v0.0.0-20180327071824-d34b9ff171c2/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -1065,6 +1092,8 @@ github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= +github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= @@ -1111,7 +1140,6 @@ github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vq github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -1155,6 +1183,7 @@ github.com/moby/sys/signal v0.6.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs= github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= +github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= @@ -1195,10 +1224,9 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/odpf/salt v0.0.0-20220614042821-c5613a78b4d6 h1:ZPATgfcee1Vn2h9Ea6A0MsqgoUCKvN3AwYOaudi6X/s= -github.com/odpf/salt v0.0.0-20220614042821-c5613a78b4d6/go.mod h1:ZY9yXIHZYlYbQGKV5WSGmyYnw+NtQEzl6z0Tj0dto8M= -github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= @@ -1242,6 +1270,7 @@ github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rm github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= +github.com/opencontainers/runc v1.1.2/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= @@ -1254,6 +1283,7 @@ github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3 github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/ory/dockertest/v3 v3.9.1/go.mod h1:42Ir9hmvaAPm0Mgibk6mBPi7SFvTXxEcnztDYOJ//uM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= @@ -1265,6 +1295,8 @@ github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2 github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4/v4 v4.1.8/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= +github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= @@ -1317,6 +1349,8 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/raystack/salt v0.3.2 h1:0kEuaHefwyL+uTC8r+jVoDW9C3jRTl6/ezZwynlc8oE= +github.com/raystack/salt v0.3.2/go.mod h1:MZUZG25Si+aU8QkqGt9FZrHA7zm5gQGnzRk5HRq9jaE= github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -1334,6 +1368,7 @@ github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/f github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -1348,11 +1383,14 @@ github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= +github.com/segmentio/kafka-go v0.4.39 h1:75smaomhvkYRwtuOwqLsdhgCG30B82NsbdkdDfFbvrw= +github.com/segmentio/kafka-go v0.4.39/go.mod h1:T0MLgygYvmqmBvC+s8aCcbVNfJN4znVne5j0Pzowp/Q= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= @@ -1379,8 +1417,8 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= -github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= +github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= +github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -1457,9 +1495,14 @@ github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVT github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= +github.com/xdg/scram v1.0.5 h1:TuS0RFmt5Is5qm9Tm2SoD89OPqe4IRiFtyFY4iwWXsw= +github.com/xdg/scram v1.0.5/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v1.0.3 h1:cmL5Enob4W83ti/ZHuZLuKD/xqJfus4fVPwE+/BDm+4= +github.com/xdg/stringprep v1.0.3/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= @@ -1471,17 +1514,17 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.3/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1 h1:/vn0k+RBvwlxEmP5E7SZMqNxPhfMVFEJiykr15/0XKM= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os= github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= +github.com/zalando/go-keyring v0.2.3 h1:v9CUu9phlABObO4LPWycf+zwMG7nlbb3t/B5wa97yms= +github.com/zalando/go-keyring v0.2.3/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE= -go.buf.build/odpf/gw/grpc-ecosystem/grpc-gateway v1.1.35/go.mod h1:/LuddrGPi0fwj7ay6Orutt8oFfPz8Y3c8qdBkacJq1A= -go.buf.build/odpf/gw/odpf/proton v1.1.9/go.mod h1:I9E8CF7w/690vRNWqBU6qDcUbi3Pi2THdn1yycBVTDQ= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= @@ -1608,6 +1651,7 @@ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211115234514-b4de73f9ece8/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1657,6 +1701,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1731,6 +1776,14 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220401154927-543a649e0bdd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220706163947-c90051bbdb60/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220919232410-f2f64ebce3c1/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b h1:tvrvnPFcdzp294diPnrdZZZ8XUt2Tyj7svb7X52iDuU= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1756,6 +1809,10 @@ golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1770,6 +1827,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1906,10 +1965,20 @@ golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220317061510-51cd9980dadf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220405210540-1e041c57c461/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -2021,12 +2090,16 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= @@ -2080,6 +2153,10 @@ google.golang.org/api v0.69.0/go.mod h1:boanBiw+h5c3s+tBPgEzLDRHfFLWV0qXxRHz3ws7 google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= +google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= +google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= google.golang.org/api v0.103.0 h1:9yuVqlu2JCvcLg9p8S3fcFLZij8EPSyvODIY1rkMizQ= google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= google.golang.org/appengine v1.0.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= @@ -2141,6 +2218,7 @@ google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210429181445-86c259c2b4ab/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= @@ -2188,6 +2266,17 @@ google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2 google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= google.golang.org/genproto v0.0.0-20220401170504-314d38edb7de/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029 h1:zS8DNtiDX68/osEpazR86KM1vnDELdnRgpK6/fwlQTs= google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= @@ -2226,9 +2315,14 @@ google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ5 google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0/go.mod h1:DNq5QpG7LJqD2AamLZ7zvKE0DEpVl2BSEVjFycAAjRY= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -2287,22 +2381,14 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/datatypes v1.0.0/go.mod h1:aKpJ+RNhLXWeF5OAdxfzBwT1UPw1wseSchF0AY3/lSw= -gorm.io/driver/mysql v1.0.3/go.mod h1:twGxftLBlFgNVNakL7F+P/x9oYqoymG3YYT8cAfI9oI= -gorm.io/driver/postgres v1.0.5/go.mod h1:qrD92UurYzNctBMVCJ8C3VQEjffEuphycXtxOudXNCA= gorm.io/driver/postgres v1.0.8/go.mod h1:4eOzrI1MUfm6ObJU/UcmbXyiHSs8jSwH95G5P5dxcAg= -gorm.io/driver/sqlite v1.1.3/go.mod h1:AKDgRWk8lcSQSw+9kxCJnX/yySj8G3rdwYlU57cB45c= -gorm.io/driver/sqlserver v1.0.5/go.mod h1:WI/bfZ+s9TigYXe3hb3XjNaUP0TqmTdXl11pECyLATs= -gorm.io/gorm v1.20.1/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= -gorm.io/gorm v1.20.2/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= -gorm.io/gorm v1.20.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= -gorm.io/gorm v1.20.5/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= gorm.io/gorm v1.20.12/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= gorm.io/gorm v1.21.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ= +gotest.tools/v3 v3.2.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A= 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= diff --git a/internal/compiler/engine.go b/internal/compiler/engine.go index 7774028758..900f2ea28a 100644 --- a/internal/compiler/engine.go +++ b/internal/compiler/engine.go @@ -2,11 +2,12 @@ package compiler import ( "bytes" + "fmt" "strings" "text/template" "time" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/internal/errors" ) const ( @@ -39,13 +40,15 @@ func (e *Engine) Compile(templateMap map[string]string, context map[string]any) for name, content := range templateMap { tmpl, err := e.baseTemplate.New(name).Parse(content) if err != nil { - return nil, errors.AddErrContext(err, EntityCompiler, "unable to parse content for "+name) + msg := fmt.Sprintf("unable to parse content for %s: %s", name, err.Error()) + return nil, errors.InvalidArgument(EntityCompiler, msg) } var buf bytes.Buffer err = tmpl.Execute(&buf, context) if err != nil { - return nil, errors.AddErrContext(err, EntityCompiler, "unable to render content for "+name) + msg := fmt.Sprintf("unable to render content for %s: %s", name, err.Error()) + return nil, errors.InvalidArgument(EntityCompiler, msg) } rendered[name] = strings.TrimSpace(buf.String()) } diff --git a/internal/compiler/engine_test.go b/internal/compiler/engine_test.go index b2c54679e0..8587262228 100644 --- a/internal/compiler/engine_test.go +++ b/internal/compiler/engine_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/internal/compiler" + "github.com/raystack/optimus/internal/compiler" ) func TestEngine(t *testing.T) { @@ -99,7 +99,8 @@ func TestEngine(t *testing.T) { _, err := comp.Compile(map[string]string{"query": input}, context) assert.NotNil(t, err) - assert.EqualError(t, err, "internal error for entity compiler: unable to parse content for query") + assert.EqualError(t, err, "invalid argument for entity compiler: unable to parse content for query:"+ + " template: query:1: bad character U+0022 '\"'") }) t.Run("returns error when rendering fails", func(t *testing.T) { input := `event_timestamp > "{{.DSTART | Date }}"` @@ -111,7 +112,9 @@ func TestEngine(t *testing.T) { _, err := comp.Compile(map[string]string{"query": input}, context) assert.NotNil(t, err) - assert.EqualError(t, err, "internal error for entity compiler: unable to render content for query") + assert.EqualError(t, err, "invalid argument for entity compiler: unable to render content for query:"+ + " template: query:1:31: executing \"query\" at : error calling Date: parsing time \"\" as "+ + "\"2006-01-02T15:04:05Z07:00\": cannot parse \"\" as \"2006\"") }) t.Run("returns rendered string with values of macros for template map", func(t *testing.T) { testCases := []struct { diff --git a/internal/compiler/template_context.go b/internal/compiler/template_context.go index ce0ed378c7..22755da1e6 100644 --- a/internal/compiler/template_context.go +++ b/internal/compiler/template_context.go @@ -3,7 +3,7 @@ package compiler import ( "fmt" - "github.com/odpf/optimus/internal/utils" + "github.com/raystack/optimus/internal/utils" ) type ContextOpts struct { diff --git a/internal/compiler/template_context_test.go b/internal/compiler/template_context_test.go index 4234b1fa2f..8aa573be9f 100644 --- a/internal/compiler/template_context_test.go +++ b/internal/compiler/template_context_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/internal/compiler" + "github.com/raystack/optimus/internal/compiler" ) func TestContextBuilder(t *testing.T) { diff --git a/internal/compiler/template_func_test.go b/internal/compiler/template_func_test.go index d9febddc9e..5f0b3d0984 100644 --- a/internal/compiler/template_func_test.go +++ b/internal/compiler/template_func_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/internal/compiler" + "github.com/raystack/optimus/internal/compiler" ) func TestTemplateFunctions(t *testing.T) { diff --git a/internal/errors/errors.go b/internal/errors/errors.go index 34d286001a..1372a1e87a 100644 --- a/internal/errors/errors.go +++ b/internal/errors/errors.go @@ -125,12 +125,12 @@ func As(err error, target any) bool { } func (e *DomainError) Error() string { - subError := "" - if errors.Is(e.WrappedErr, &DomainError{}) { - subError = ": " + e.WrappedErr.Error() + if e.WrappedErr != nil { + return fmt.Sprintf("%v for entity %v: %v: %s", + e.ErrorType.String(), e.Entity, e.Message, e.WrappedErr.Error()) } - return fmt.Sprintf("%v for entity %v: %v%s", - e.ErrorType.String(), e.Entity, e.Message, subError) + return fmt.Sprintf("%v for entity %v: %v", + e.ErrorType.String(), e.Entity, e.Message) } func (e *DomainError) Unwrap() error { diff --git a/internal/errors/errors_test.go b/internal/errors/errors_test.go index f543c85fe6..711dde9969 100644 --- a/internal/errors/errors_test.go +++ b/internal/errors/errors_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/internal/errors" ) func TestErrors(t *testing.T) { diff --git a/internal/errors/multi_test.go b/internal/errors/multi_test.go index 7c1b96ef24..859d540bce 100644 --- a/internal/errors/multi_test.go +++ b/internal/errors/multi_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/internal/errors" ) func TestMultiError(t *testing.T) { @@ -14,13 +14,13 @@ func TestMultiError(t *testing.T) { me := errors.NewMultiError("multi") me.Append(nil) - assert.Nil(t, errors.MultiToError(me)) + assert.Nil(t, me.ToErr()) }) t.Run("adds other type of error", func(t *testing.T) { me := errors.NewMultiError("multi") me.Append(errors.NotFound("dummy", "not found")) - assert.NotNil(t, errors.MultiToError(me)) + assert.NotNil(t, me.ToErr()) assert.EqualError(t, me, "multi:\n not found for entity dummy: not found") }) t.Run("adds errors in multi error to list", func(t *testing.T) { @@ -30,7 +30,7 @@ func TestMultiError(t *testing.T) { me := errors.NewMultiError("top level error") me.Append(me1) - assert.NotNil(t, errors.MultiToError(me)) + assert.NotNil(t, me.ToErr()) assert.EqualError(t, me, "top level error:\n not found for entity dummy: not found") }) }) diff --git a/internal/lib/cron/cron_test.go b/internal/lib/cron/cron_test.go index 4305547f03..21cb6354e3 100644 --- a/internal/lib/cron/cron_test.go +++ b/internal/lib/cron/cron_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/internal/lib/cron" + "github.com/raystack/optimus/internal/lib/cron" ) func TestScheduleSpec(t *testing.T) { diff --git a/internal/lib/progress/progress.go b/internal/lib/progress/progress.go deleted file mode 100644 index 3b8d8da2ca..0000000000 --- a/internal/lib/progress/progress.go +++ /dev/null @@ -1,32 +0,0 @@ -package progress - -import "fmt" - -// Event an event published by certain services -// that notify that a certain progress has been made -type Event interface { - fmt.Stringer -} - -// Observer is an entity that wishes to -// receive progress events -type Observer interface { - Notify(evt Event) -} - -// ObserverChain iterate on all the observers for notify -type ObserverChain struct { - obs []Observer -} - -// Notify each observer -func (chain *ObserverChain) Notify(evt Event) { - for _, ob := range chain.obs { - ob.Notify(evt) - } -} - -// Join will add observer to listen for notify events -func (chain *ObserverChain) Join(obs Observer) { - chain.obs = append(chain.obs, obs) -} diff --git a/internal/lib/set/set.go b/internal/lib/set/set.go deleted file mode 100644 index f5120c9bcb..0000000000 --- a/internal/lib/set/set.go +++ /dev/null @@ -1,59 +0,0 @@ -package set - -import ( - "time" - - "github.com/emirpasic/gods/sets/hashset" - "github.com/emirpasic/gods/sets/treeset" -) - -// Set is a data structure implementation of ordered or unordered -// collection of unique elements that should insert/delete in O(1) if unordered -// and insert/delete in O(logn) if ordered -type Set interface { - Add(items ...interface{}) - Remove(items ...interface{}) - Values() []interface{} - String() string - Clear() - Size() int - Empty() bool - Contains(items ...interface{}) bool -} - -// Comparator will make type assertion (see IntComparator for example), -// which will panic if a or b are not of the asserted type. -// -// Should return a number: -// -// negative , if a < b -// zero , if a == b -// positive , if a > b -type Comparator func(a, b interface{}) int - -// NewHashSet instantiates a new empty set of unordered elements -func NewHashSet() *hashset.Set { - return hashset.New() -} - -// NewTreeSetWithTimeComparator instantiates a new empty set of ordered elements that contain time.Time -func NewTreeSetWithTimeComparator() *treeset.Set { - return treeset.NewWith(timeComparator) -} - -func NewTreeSetWith(comp func(a, b interface{}) int) *treeset.Set { - return treeset.NewWith(comp) -} - -func timeComparator(a, b interface{}) int { - aAsserted := a.(time.Time) - bAsserted := b.(time.Time) - switch { - case aAsserted.After(bAsserted): - return 1 - case aAsserted.Before(bAsserted): - return -1 - default: - return 0 - } -} diff --git a/internal/lib/tree/multi_root_tree_test.go b/internal/lib/tree/multi_root_tree_test.go index fa93f60088..8c48d400cf 100644 --- a/internal/lib/tree/multi_root_tree_test.go +++ b/internal/lib/tree/multi_root_tree_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" - tree2 "github.com/odpf/optimus/internal/lib/tree" + tree2 "github.com/raystack/optimus/internal/lib/tree" ) func TestMultiRootDagTree(t *testing.T) { diff --git a/internal/lib/tree/tree_node.go b/internal/lib/tree/tree_node.go index 4eb4b69b8c..a17479d227 100644 --- a/internal/lib/tree/tree_node.go +++ b/internal/lib/tree/tree_node.go @@ -1,9 +1,5 @@ package tree -import ( - "github.com/odpf/optimus/internal/lib/set" -) - type TreeData interface { GetName() string } @@ -12,7 +8,6 @@ type TreeData interface { type TreeNode struct { Data TreeData Dependents []*TreeNode - Runs set.Set } // GetAllNodes returns level order traversal of tree starting from current node @@ -43,6 +38,5 @@ func NewTreeNode(data TreeData) *TreeNode { return &TreeNode{ Data: data, Dependents: []*TreeNode{}, - Runs: set.NewTreeSetWithTimeComparator(), } } diff --git a/internal/lib/tree/tree_node_test.go b/internal/lib/tree/tree_node_test.go index e918d48f2c..949c5f5314 100644 --- a/internal/lib/tree/tree_node_test.go +++ b/internal/lib/tree/tree_node_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/internal/lib/tree" + "github.com/raystack/optimus/internal/lib/tree" ) type testNode struct { diff --git a/internal/models/plugin.go b/internal/models/plugin.go index 011f74153a..fbc2ad22e6 100644 --- a/internal/models/plugin.go +++ b/internal/models/plugin.go @@ -6,7 +6,7 @@ import ( "fmt" "sort" - "github.com/odpf/optimus/sdk/plugin" + "github.com/raystack/optimus/sdk/plugin" ) var ErrUnsupportedPlugin = errors.New("unsupported plugin requested, make sure its correctly installed") diff --git a/internal/models/plugin_test.go b/internal/models/plugin_test.go index c05ce97602..a48fb2c4e0 100644 --- a/internal/models/plugin_test.go +++ b/internal/models/plugin_test.go @@ -5,9 +5,9 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/internal/models" - "github.com/odpf/optimus/sdk/plugin" - mockPlugin "github.com/odpf/optimus/sdk/plugin/mock" + "github.com/raystack/optimus/internal/models" + "github.com/raystack/optimus/sdk/plugin" + mockPlugin "github.com/raystack/optimus/sdk/plugin/mock" ) func TestPluginModels(t *testing.T) { diff --git a/internal/models/window_test.go b/internal/models/window_test.go index 09249e0a4f..e4963c89f1 100644 --- a/internal/models/window_test.go +++ b/internal/models/window_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/internal/models" + "github.com/raystack/optimus/internal/models" ) func TestNewWindow(t *testing.T) { diff --git a/internal/models/window_v1_test.go b/internal/models/window_v1_test.go index 5a9bfe2f83..d3e6c89745 100644 --- a/internal/models/window_v1_test.go +++ b/internal/models/window_v1_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/internal/models" + "github.com/raystack/optimus/internal/models" ) func TestWindowV1(t *testing.T) { diff --git a/internal/models/window_v2.go b/internal/models/window_v2.go index e4369815c2..6276bc4cd3 100644 --- a/internal/models/window_v2.go +++ b/internal/models/window_v2.go @@ -6,7 +6,7 @@ import ( "strings" "time" - "github.com/odpf/optimus/internal/utils" + "github.com/raystack/optimus/internal/utils" ) type windowV2 struct { diff --git a/internal/models/window_v2_test.go b/internal/models/window_v2_test.go index dedb98f330..58d699a45d 100644 --- a/internal/models/window_v2_test.go +++ b/internal/models/window_v2_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/internal/models" + "github.com/raystack/optimus/internal/models" ) func TestWindowV2(t *testing.T) { diff --git a/internal/store/postgres/job/adapter.go b/internal/store/postgres/job/adapter.go index 0beee867e1..f4d575b4b6 100644 --- a/internal/store/postgres/job/adapter.go +++ b/internal/store/postgres/job/adapter.go @@ -9,9 +9,9 @@ import ( "github.com/jackc/pgx/v5" "github.com/lib/pq" - "github.com/odpf/optimus/core/job" - "github.com/odpf/optimus/internal/errors" - "github.com/odpf/optimus/internal/models" + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/models" ) const jobDatetimeLayout = "2006-01-02" @@ -57,7 +57,6 @@ type Schedule struct { EndDate *time.Time `json:",omitempty"` Interval string DependsOnPast bool - CatchUp bool Retry *Retry } @@ -266,7 +265,6 @@ func toStorageSchedule(scheduleSpec *job.Schedule) ([]byte, error) { StartDate: startDate, Interval: scheduleSpec.Interval(), DependsOnPast: scheduleSpec.DependsOnPast(), - CatchUp: scheduleSpec.CatchUp(), Retry: retry, } if scheduleSpec.EndDate() != "" { @@ -462,7 +460,6 @@ func fromStorageSchedule(raw []byte) (*job.Schedule, error) { return nil, err } scheduleBuilder := job.NewScheduleBuilder(startDate). - WithCatchUp(storageSchedule.CatchUp). WithDependsOnPast(storageSchedule.DependsOnPast). WithInterval(storageSchedule.Interval) diff --git a/internal/store/postgres/job/job_repository.go b/internal/store/postgres/job/job_repository.go index c273a6fe9b..c6a9d2a6f9 100644 --- a/internal/store/postgres/job/job_repository.go +++ b/internal/store/postgres/job/job_repository.go @@ -4,15 +4,16 @@ import ( "context" "database/sql" "fmt" + "strings" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgconn" "github.com/jackc/pgx/v5/pgxpool" - "github.com/odpf/optimus/core/job" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" ) const ( @@ -40,7 +41,7 @@ func (j JobRepository) Add(ctx context.Context, jobs []*job.Job) ([]*job.Job, er } storedJobs = append(storedJobs, jobEntity) } - return storedJobs, errors.MultiToError(me) + return storedJobs, me.ToErr() } func (j JobRepository) insertJobSpec(ctx context.Context, jobEntity *job.Job) error { @@ -101,7 +102,125 @@ func (j JobRepository) Update(ctx context.Context, jobs []*job.Job) ([]*job.Job, } storedJobs = append(storedJobs, jobEntity) } - return storedJobs, errors.MultiToError(me) + return storedJobs, me.ToErr() +} + +func (j JobRepository) UpdateState(ctx context.Context, jobTenant tenant.Tenant, jobNames []job.Name, jobState job.State, remark string) error { + updateJobStateQuery := ` +UPDATE job SET state = $1, remark = $2, updated_at = NOW() +WHERE project_name = $4 AND namespace_name = $5 AND name = any ($3);` + + tag, err := j.db.Exec(ctx, updateJobStateQuery, jobState, remark, jobNames, jobTenant.ProjectName(), jobTenant.NamespaceName()) + if err != nil { + return errors.Wrap(job.EntityJob, "error during job state update", err) + } + if tag.RowsAffected() != int64(len(jobNames)) { + return errors.NewError(errors.ErrNotFound, job.EntityJob, "failed to update state of all of the selected job in DB") + } + return nil +} + +func (j JobRepository) SyncState(ctx context.Context, jobTenant tenant.Tenant, disabledJobNames, enabledJobNames []job.Name) error { + tx, err := j.db.Begin(ctx) + if err != nil { + return err + } + updateJobStateQuery := ` +UPDATE job SET state = $1 +WHERE project_name = $2 AND namespace_name = $3 AND name = any ($4);` + + _, err = tx.Exec(ctx, updateJobStateQuery, job.ENABLED, jobTenant.ProjectName(), jobTenant.NamespaceName(), enabledJobNames) + if err != nil { + tx.Rollback(ctx) + return errors.Wrap(job.EntityJob, "error during job state enable sync", err) + } + + _, err = j.db.Exec(ctx, updateJobStateQuery, job.DISABLED, jobTenant.ProjectName(), jobTenant.NamespaceName(), disabledJobNames) + if err != nil { + tx.Rollback(ctx) + return errors.Wrap(job.EntityJob, "error during job state disable sync", err) + } + + tx.Commit(ctx) + return nil +} + +func (j JobRepository) ChangeJobNamespace(ctx context.Context, jobName job.Name, tenant, newTenant tenant.Tenant) error { + tx, err := j.db.Begin(ctx) + if err != nil { + return errors.InternalError(job.EntityJob, "unable to begin transaction", err) + } + + if err = changeJobNamespace(ctx, tx, jobName, tenant, newTenant); err != nil { + tx.Rollback(ctx) + return err + } + if err = changeJobUpstreamNamespace(ctx, tx, jobName, tenant, newTenant); err != nil { + tx.Rollback(ctx) + return err + } + if err = changeJobRunNamespace(ctx, tx, jobName, tenant, newTenant); err != nil { + tx.Rollback(ctx) + return err + } + tx.Commit(ctx) + return nil +} + +func changeJobNamespace(ctx context.Context, tx pgx.Tx, jobName job.Name, tenant, newTenant tenant.Tenant) error { + changeJobNamespaceQuery := ` +UPDATE job SET + namespace_name = $1, + updated_at = NOW(), deleted_at = null +WHERE + name = $2 AND + project_name = $3 AND + namespace_name = $4 +;` + tag, err := tx.Exec(ctx, changeJobNamespaceQuery, newTenant.NamespaceName(), jobName, + tenant.ProjectName(), tenant.NamespaceName()) + if err != nil { + return errors.Wrap(job.EntityJob, err.Error(), err) + } + + if tag.RowsAffected() == 0 { + return errors.NotFound(job.EntityJob, "job not found with the given namespace: "+tenant.NamespaceName().String()) + } + return nil +} + +func changeJobUpstreamNamespace(ctx context.Context, tx pgx.Tx, jobName job.Name, tenant, newTenant tenant.Tenant) error { + changeJobUpstreamsQuery := ` +UPDATE job_upstream SET + upstream_namespace_name = $1 +WHERE + upstream_job_name = $2 AND + upstream_project_name = $3 AND + upstream_namespace_name = $4 +;` + _, err := tx.Exec(ctx, changeJobUpstreamsQuery, newTenant.NamespaceName(), jobName, + tenant.ProjectName(), tenant.NamespaceName()) + if err != nil { + return errors.Wrap(job.EntityJob, err.Error(), err) + } + return nil +} + +func changeJobRunNamespace(ctx context.Context, tx pgx.Tx, jobName job.Name, tenant, newTenant tenant.Tenant) error { + changeJobRunNamespaceQuery := ` +UPDATE job_run SET + namespace_name = $1 +WHERE + job_name = $2 AND + project_name = $3 AND + namespace_name = $4 +;` + _, err := tx.Exec(ctx, changeJobRunNamespaceQuery, newTenant.NamespaceName(), jobName, + tenant.ProjectName(), tenant.NamespaceName()) + if err != nil { + return errors.Wrap(job.EntityJob, err.Error(), err) + } + return nil } func (j JobRepository) preCheckUpdate(ctx context.Context, jobEntity *job.Job) error { @@ -273,7 +392,7 @@ func (j JobRepository) toJobNameWithUpstreams(storeJobsWithUpstreams []*JobWithU jobNameWithUpstreams[name] = upstreams } - if err := errors.MultiToError(me); err != nil { + if err := me.ToErr(); err != nil { return nil, err } return jobNameWithUpstreams, nil @@ -477,10 +596,10 @@ func (j *JobWithUpstream) getJobFullName() string { } func (j JobRepository) ReplaceUpstreams(ctx context.Context, jobsWithUpstreams []*job.WithUpstream) error { - var storageJobUpstreams []*JobWithUpstream + var jobUpstreams []*JobWithUpstream for _, jobWithUpstreams := range jobsWithUpstreams { - upstream := toJobUpstream(jobWithUpstreams) - storageJobUpstreams = append(storageJobUpstreams, upstream...) + singleJobUpstreams := toJobUpstream(jobWithUpstreams) + jobUpstreams = append(jobUpstreams, singleJobUpstreams...) } tx, err := j.db.Begin(ctx) @@ -489,15 +608,15 @@ func (j JobRepository) ReplaceUpstreams(ctx context.Context, jobsWithUpstreams [ } var jobFullName []string - for _, upstream := range storageJobUpstreams { - jobFullName = append(jobFullName, upstream.getJobFullName()) + for _, jobWithUpstream := range jobsWithUpstreams { + jobFullName = append(jobFullName, jobWithUpstream.Job().FullName()) } - if err = j.deleteUpstreams(ctx, tx, jobFullName); err != nil { + if err = j.deleteUpstreamsByJobNames(ctx, tx, jobFullName); err != nil { tx.Rollback(ctx) return err } - if err = j.insertUpstreams(ctx, tx, storageJobUpstreams); err != nil { + if err = j.insertUpstreams(ctx, tx, jobUpstreams); err != nil { tx.Rollback(ctx) return err } @@ -568,7 +687,7 @@ VALUES ( return nil } -func (JobRepository) deleteUpstreams(ctx context.Context, tx pgx.Tx, jobUpstreams []string) error { +func (JobRepository) deleteUpstreamsByJobNames(ctx context.Context, tx pgx.Tx, jobUpstreams []string) error { deleteForProjectScope := `DELETE FROM job_upstream WHERE project_name || '/' || job_name = any ($1);` @@ -693,7 +812,7 @@ func (j JobRepository) GetAllByTenant(ctx context.Context, jobTenant tenant.Tena jobs = append(jobs, jobSpec) } - return jobs, errors.MultiToError(me) + return jobs, me.ToErr() } func (j JobRepository) GetUpstreams(ctx context.Context, projectName tenant.ProjectName, jobName job.Name) ([]*job.Upstream, error) { @@ -810,5 +929,49 @@ func fromStoreDownstream(storeDownstreamList []Downstream) ([]*job.Downstream, e } downstreamList = append(downstreamList, job.NewDownstream(downstreamJobName, downstreamProjectName, downstreamNamespaceName, downstreamTaskName)) } - return downstreamList, errors.MultiToError(me) + return downstreamList, me.ToErr() +} + +func (j JobRepository) GetDownstreamBySources(ctx context.Context, sources []job.ResourceURN) ([]*job.Downstream, error) { + if len(sources) == 0 { + return nil, nil + } + + var sourceWhereStatements []string + for _, r := range sources { + statement := "'" + r.String() + "' = any(sources)" + sourceWhereStatements = append(sourceWhereStatements, statement) + } + sourceStatement := "(" + strings.Join(sourceWhereStatements, " or \n") + ")" + + queryBuilder := new(strings.Builder) + queryBuilder.WriteString(` +SELECT + name as job_name, project_name, namespace_name, task_name +FROM job +WHERE +deleted_at IS NULL and +`) + queryBuilder.WriteString(sourceStatement) + queryBuilder.WriteString(";\n") + + query := queryBuilder.String() + + rows, err := j.db.Query(ctx, query) + if err != nil { + return nil, errors.Wrap(job.EntityJob, "error while getting job downstream", err) + } + defer rows.Close() + + var storeDownstream []Downstream + for rows.Next() { + var downstream Downstream + err := rows.Scan(&downstream.JobName, &downstream.ProjectName, &downstream.NamespaceName, &downstream.TaskName) + if err != nil { + return nil, errors.Wrap(job.EntityJob, "error while getting downstream by destination", err) + } + storeDownstream = append(storeDownstream, downstream) + } + + return fromStoreDownstream(storeDownstream) } diff --git a/internal/store/postgres/job/job_repository_test.go b/internal/store/postgres/job/job_repository_test.go index 971435156e..5642b7792b 100644 --- a/internal/store/postgres/job/job_repository_test.go +++ b/internal/store/postgres/job/job_repository_test.go @@ -9,12 +9,12 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/job" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/models" - postgres "github.com/odpf/optimus/internal/store/postgres/job" - tenantPostgres "github.com/odpf/optimus/internal/store/postgres/tenant" - "github.com/odpf/optimus/tests/setup" + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/models" + postgres "github.com/raystack/optimus/internal/store/postgres/job" + tenantPostgres "github.com/raystack/optimus/internal/store/postgres/tenant" + "github.com/raystack/optimus/tests/setup" ) func TestPostgresJobRepository(t *testing.T) { @@ -707,6 +707,29 @@ func TestPostgresJobRepository(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, []*job.Upstream{upstreamC}, upstreamsOfJobA) }) + t.Run("deletes existing job upstream without inserts if no longer upstream found", func(t *testing.T) { + db := dbSetup() + + upstreamB := job.NewUpstreamResolved("jobB", host, "resource-B", sampleTenant, upstreamType, taskName, false) + + jobUpstreamRepo := postgres.NewJobRepository(db) + _, err = jobUpstreamRepo.Add(ctx, []*job.Job{jobA, jobB, jobC}) + assert.NoError(t, err) + + jobAWithUpstream := job.NewWithUpstream(jobA, []*job.Upstream{upstreamB}) + assert.NoError(t, jobUpstreamRepo.ReplaceUpstreams(ctx, []*job.WithUpstream{jobAWithUpstream})) + + upstreamsOfJobA, err := jobUpstreamRepo.GetUpstreams(ctx, proj.Name(), jobA.Spec().Name()) + assert.NoError(t, err) + assert.EqualValues(t, []*job.Upstream{upstreamB}, upstreamsOfJobA) + + jobAWithNoUpstream := job.NewWithUpstream(jobA, []*job.Upstream{}) + assert.NoError(t, jobUpstreamRepo.ReplaceUpstreams(ctx, []*job.WithUpstream{jobAWithNoUpstream})) + + upstreamsOfJobA, err = jobUpstreamRepo.GetUpstreams(ctx, proj.Name(), jobA.Spec().Name()) + assert.NoError(t, err) + assert.Empty(t, upstreamsOfJobA) + }) t.Run("inserts job upstreams with exact name across projects exists", func(t *testing.T) { db := dbSetup() @@ -730,6 +753,49 @@ func TestPostgresJobRepository(t *testing.T) { assert.Nil(t, jobUpstreamRepo.ReplaceUpstreams(ctx, []*job.WithUpstream{jobWithUpstream})) }) }) + t.Run("ChangeJobNamespace", func(t *testing.T) { + newTenant, _ := tenant.NewTenant(otherNamespace.ProjectName().String(), otherNamespace.Name().String()) + jobSpecA, err := job.NewSpecBuilder(jobVersion, "sample-job-A", jobOwner, jobSchedule, jobWindow, jobTask).WithDescription(jobDescription).Build() + assert.NoError(t, err) + jobA := job.NewJob(sampleTenant, jobSpecA, "dev.resource.sample_a", []job.ResourceURN{"dev.resource.sample_c"}) + + jobSpecB, err := job.NewSpecBuilder(jobVersion, "sample-job-B", jobOwner, jobSchedule, jobWindow, jobTask).WithDescription(jobDescription).Build() + assert.NoError(t, err) + jobB := job.NewJob(sampleTenant, jobSpecB, "dev.resource.sample_b", []job.ResourceURN{"dev.resource.sample_a"}) + + t.Run("Change Job namespace successfully", func(t *testing.T) { + db := dbSetup() + + jobRepo := postgres.NewJobRepository(db) + addedJob, err := jobRepo.Add(ctx, []*job.Job{jobA, jobB}) + assert.NoError(t, err) + assert.NotNil(t, addedJob) + + upstreamAInferred := job.NewUpstreamResolved("sample-job-A", "host-1", "dev.resource.sample_a", sampleTenant, "inferred", taskName, false) + jobBWithUpstream := job.NewWithUpstream(jobB, []*job.Upstream{upstreamAInferred}) + err = jobRepo.ReplaceUpstreams(ctx, []*job.WithUpstream{jobBWithUpstream}) + assert.NoError(t, err) + + // update failure with proper log message shows job has been soft deleted + err = jobRepo.ChangeJobNamespace(ctx, jobSpecA.Name(), sampleTenant, newTenant) + assert.Nil(t, err) + + jobA, err = jobRepo.GetByJobName(ctx, proj.Name(), jobSpecA.Name()) + assert.Nil(t, err) + assert.Equal(t, jobA.Tenant().NamespaceName(), newTenant.NamespaceName()) + + jobBUpstreams, err := jobRepo.GetUpstreams(ctx, proj.Name(), jobSpecB.Name()) + assert.Nil(t, err) + jobAIsUpstream := false + for _, upstream := range jobBUpstreams { + if upstream.Name() == jobSpecA.Name() { + jobAIsUpstream = true + assert.Equal(t, upstream.NamespaceName(), newTenant.NamespaceName()) + } + } + assert.True(t, jobAIsUpstream) + }) + }) t.Run("Delete", func(t *testing.T) { t.Run("soft delete a job if not asked to do clean delete", func(t *testing.T) { @@ -1059,4 +1125,75 @@ func TestPostgresJobRepository(t *testing.T) { assert.EqualValues(t, expectedDownstream, result) }) }) + + t.Run("GetDownstreamBySources", func(t *testing.T) { + t.Run("returns empty downstream if resource urns are empty", func(t *testing.T) { + db := dbSetup() + jobRepo := postgres.NewJobRepository(db) + + var sources []job.ResourceURN + + result, err := jobRepo.GetDownstreamBySources(ctx, sources) + assert.NoError(t, err) + assert.Empty(t, result) + }) + + t.Run("returns downstream given resource urns", func(t *testing.T) { + db := dbSetup() + + jobAName, _ := job.NameFrom("sample-job-A") + jobSpecA, err := job.NewSpecBuilder(jobVersion, jobAName, jobOwner, jobSchedule, jobWindow, jobTask).Build() + assert.NoError(t, err) + jobA := job.NewJob(sampleTenant, jobSpecA, "dev.resource.sample_a", []job.ResourceURN{"dev.resource.sample_b", "dev.resource.sample_c"}) + + jobBName, _ := job.NameFrom("sample-job-b") + jobSpecB, err := job.NewSpecBuilder(jobVersion, jobBName, jobOwner, jobSchedule, jobWindow, jobTask).Build() + assert.NoError(t, err) + jobB := job.NewJob(sampleTenant, jobSpecB, "dev.resource.sample_d", []job.ResourceURN{"dev.resource.sample_e"}) + + jobCName, _ := job.NameFrom("sample-job-c") + jobSpecC, err := job.NewSpecBuilder(jobVersion, jobCName, jobOwner, jobSchedule, jobWindow, jobTask).Build() + assert.NoError(t, err) + jobC := job.NewJob(sampleTenant, jobSpecC, "dev.resource.sample_f", nil) + + jobRepo := postgres.NewJobRepository(db) + _, err = jobRepo.Add(ctx, []*job.Job{jobA, jobB, jobC}) + assert.NoError(t, err) + + jobAAsDownstream := job.NewDownstream(jobAName, sampleTenant.ProjectName(), sampleTenant.NamespaceName(), jobTask.Name()) + jobBAsDownstream := job.NewDownstream(jobBName, sampleTenant.ProjectName(), sampleTenant.NamespaceName(), jobTask.Name()) + + testCases := []struct { + sources []job.ResourceURN + expectedDownstreams []*job.Downstream + }{ + { + sources: []job.ResourceURN{"dev.resource.sample_b"}, + expectedDownstreams: []*job.Downstream{jobAAsDownstream}, + }, + { + sources: []job.ResourceURN{"dev.resource.sample_b", "dev.resource.sample_e"}, + expectedDownstreams: []*job.Downstream{jobAAsDownstream, jobBAsDownstream}, + }, + { + sources: []job.ResourceURN{"dev.resource.sample_b", "dev.resource.sample_c"}, + expectedDownstreams: []*job.Downstream{jobAAsDownstream}, + }, + { + sources: []job.ResourceURN{"dev.resource.sample_e", "dev.resource.sample_f"}, + expectedDownstreams: []*job.Downstream{jobBAsDownstream}, + }, + { + sources: []job.ResourceURN{"dev.resource.sample_f", "dev.resource.sample_g"}, + expectedDownstreams: nil, + }, + } + + for _, test := range testCases { + result, err := jobRepo.GetDownstreamBySources(ctx, test.sources) + assert.NoError(t, err) + assert.EqualValues(t, test.expectedDownstreams, result) + } + }) + }) } diff --git a/internal/store/postgres/migrations/000053_cleanup_old_data.down.sql b/internal/store/postgres/migrations/000053_cleanup_old_data.down.sql new file mode 100644 index 0000000000..110a67606c --- /dev/null +++ b/internal/store/postgres/migrations/000053_cleanup_old_data.down.sql @@ -0,0 +1,170 @@ +ALTER TABLE hook_run DROP CONSTRAINT IF EXISTS hook_run_job_id_fkey; +ALTER TABLE hook_run ADD CONSTRAINT hook_run_job_id_fkey +FOREIGN KEY (job_run_id) REFERENCES job_run(id) ON DELETE CASCADE; + +ALTER TABLE sensor_run DROP CONSTRAINT IF EXISTS sensor_run_job_id_fkey; +ALTER TABLE sensor_run ADD CONSTRAINT sensor_run_job_id_fkey +FOREIGN KEY (job_run_id) REFERENCES job_run(id) ON DELETE CASCADE; + +ALTER TABLE task_run DROP CONSTRAINT IF EXISTS task_run_job_id_fkey; +ALTER TABLE task_run ADD CONSTRAINT task_run_job_id_fkey +FOREIGN KEY (job_run_id) REFERENCES job_run(id) ON DELETE CASCADE; + +CREATE TABLE project_old ( + id uuid DEFAULT uuid_generate_v4() NOT NULL, + name character varying(100) NOT NULL, + config jsonb, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + deleted_at timestamp with time zone +); +ALTER TABLE ONLY project_old + ADD CONSTRAINT project_name_key UNIQUE (name); +ALTER TABLE ONLY project_old + ADD CONSTRAINT project_pkey PRIMARY KEY (id); + + +CREATE TABLE namespace_old ( + id uuid DEFAULT uuid_generate_v4() NOT NULL, + project_id uuid NOT NULL, + name character varying(100) NOT NULL, + config jsonb, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + deleted_at timestamp with time zone +); +ALTER TABLE ONLY namespace_old + ADD CONSTRAINT namespace_pkey PRIMARY KEY (id); +ALTER TABLE ONLY namespace_old + ADD CONSTRAINT namespace_project_id_name_key UNIQUE (project_id, name); +ALTER TABLE ONLY namespace_old + ADD CONSTRAINT namespace_project_id_fkey FOREIGN KEY (project_id) REFERENCES project_old(id); + +CREATE TABLE secret_old ( + id uuid DEFAULT uuid_generate_v4() NOT NULL, + project_id uuid NOT NULL, + name character varying(100) NOT NULL, + value text, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + deleted_at timestamp with time zone, + namespace_id uuid, + type character varying(15) +); +ALTER TABLE ONLY secret_old + ADD CONSTRAINT secret_pkey PRIMARY KEY (id); +ALTER TABLE ONLY secret_old + ADD CONSTRAINT secret_project_id_name_key UNIQUE (project_id, name); +CREATE INDEX secret_name_idx ON secret_old USING btree (name); +CREATE INDEX secret_namespace_id_idx ON secret_old USING btree (namespace_id); +CREATE INDEX secret_project_id_idx ON secret_old USING btree (project_id); +CREATE INDEX secret_type_idx ON secret_old USING btree (type); +ALTER TABLE ONLY secret_old + ADD CONSTRAINT secret_namespace_id_fkey FOREIGN KEY (namespace_id) REFERENCES namespace_old(id); +ALTER TABLE ONLY secret_old + ADD CONSTRAINT secret_project_id_fkey FOREIGN KEY (project_id) REFERENCES project_old(id); + + +CREATE TABLE resource_old ( + id uuid DEFAULT uuid_generate_v4() NOT NULL, + project_id uuid NOT NULL, + datastore character varying(100) NOT NULL, + version integer, + name character varying(250) NOT NULL, + type character varying(100) NOT NULL, + spec bytea, + assets jsonb, + labels jsonb, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + deleted_at timestamp with time zone, + namespace_id uuid NOT NULL, + urn character varying(300) +); +ALTER TABLE ONLY resource_old + ADD CONSTRAINT resource_pkey PRIMARY KEY (id); +ALTER TABLE ONLY resource_old + ADD CONSTRAINT resource_project_id_datastore_name_key UNIQUE (project_id, datastore, name); +CREATE INDEX resource_name_idx ON resource_old USING btree (name); +CREATE INDEX resource_namespace_id_idx ON resource_old USING btree (namespace_id); +CREATE INDEX resource_project_id_idx ON resource_old USING btree (project_id); +CREATE INDEX resource_urn_idx ON resource_old USING btree (urn); +ALTER TABLE ONLY resource_old + ADD CONSTRAINT resource_namespace_id_fkey FOREIGN KEY (namespace_id) REFERENCES namespace_old(id); +ALTER TABLE ONLY resource_old + ADD CONSTRAINT resource_project_id_fkey FOREIGN KEY (project_id) REFERENCES project_old(id); + +CREATE TABLE backup_old ( + id uuid NOT NULL, + resource_id uuid NOT NULL, + spec jsonb, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL +); +ALTER TABLE ONLY backup_old + ADD CONSTRAINT backup_pkey PRIMARY KEY (id); +ALTER TABLE ONLY backup_old + ADD CONSTRAINT backup_resource_id_fkey FOREIGN KEY (resource_id) REFERENCES resource_old(id) ON DELETE CASCADE; + +CREATE TABLE replay_old ( + id uuid NOT NULL, + job_id uuid NOT NULL, + start_date timestamp with time zone NOT NULL, + end_date timestamp with time zone NOT NULL, + status character varying(30) NOT NULL, + message jsonb, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + execution_tree jsonb, + config jsonb +); +ALTER TABLE ONLY replay_old + ADD CONSTRAINT replay_pkey PRIMARY KEY (id); + +CREATE TABLE job_old ( + id uuid DEFAULT uuid_generate_v4() NOT NULL, + project_id uuid NOT NULL, + version integer, + name character varying(220) NOT NULL, + owner character varying(100), + start_date timestamp without time zone NOT NULL, + end_date timestamp without time zone, + "interval" character varying(50), + dependencies jsonb, + task_name character varying(200), + task_config jsonb, + old_window_size bigint, + old_window_offset bigint, + window_truncate_to character varying(10), + assets jsonb, + hooks jsonb, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + deleted_at timestamp with time zone, + destination character varying(300), + description text, + labels jsonb, + namespace_id uuid NOT NULL, + behavior jsonb, + metadata jsonb, + external_dependencies jsonb, + window_size character varying(10), + window_offset character varying(10) +); +ALTER TABLE ONLY job_old + ADD CONSTRAINT job_pkey PRIMARY KEY (id); +ALTER TABLE ONLY job_old + ADD CONSTRAINT job_project_id_name_key UNIQUE (project_id, name); +CREATE INDEX job_old_destination_idx ON job_old USING btree (destination); +CREATE INDEX job_old_name_idx ON job_old USING btree (name); +CREATE INDEX job_old_namespace_id_idx ON job_old USING btree (namespace_id); +CREATE INDEX job_old_project_id_idx ON job_old USING btree (project_id); +ALTER TABLE ONLY job_old + ADD CONSTRAINT job_namespace_id_fkey FOREIGN KEY (namespace_id) REFERENCES namespace_old(id); +ALTER TABLE ONLY job_old + ADD CONSTRAINT job_project_id_fkey FOREIGN KEY (project_id) REFERENCES project_old(id); + +DROP INDEX IF EXISTS idx_job_run_start_time; +DROP INDEX IF EXISTS idx_sensor_run_start_time; +DROP INDEX IF EXISTS idx_hook_run_start_time; +DROP INDEX IF EXISTS idx_task_run_start_time; \ No newline at end of file diff --git a/internal/store/postgres/migrations/000053_cleanup_old_data.up.sql b/internal/store/postgres/migrations/000053_cleanup_old_data.up.sql new file mode 100644 index 0000000000..0dad144f96 --- /dev/null +++ b/internal/store/postgres/migrations/000053_cleanup_old_data.up.sql @@ -0,0 +1,13 @@ +DROP TABLE IF EXISTS JOB_OLD; +DROP TABLE IF EXISTS JOB_DEPLOYMENT, MIGRATION_STEPS; +DROP TABLE IF EXISTS BACKUP_OLD, REPLAY_OLD, SECRET_OLD, RESOURCE_OLD; +DROP TABLE IF EXISTS NAMESPACE_OLD, PROJECT_OLD; + +ALTER TABLE sensor_run DROP CONSTRAINT IF EXISTS sensor_run_job_id_fkey; +ALTER TABLE task_run DROP CONSTRAINT IF EXISTS task_run_job_id_fkey; +ALTER TABLE hook_run DROP CONSTRAINT IF EXISTS hook_run_job_id_fkey; + +CREATE INDEX IF NOT EXISTS idx_job_run_start_time ON job_run (start_time); +CREATE INDEX IF NOT EXISTS idx_sensor_run_start_time ON sensor_run (start_time); +CREATE INDEX IF NOT EXISTS idx_hook_run_start_time ON hook_run (start_time); +CREATE INDEX IF NOT EXISTS idx_task_run_start_time ON task_run (start_time); diff --git a/internal/store/postgres/migrations/000054_job_run_constraint.down.sql b/internal/store/postgres/migrations/000054_job_run_constraint.down.sql new file mode 100644 index 0000000000..6557fa895a --- /dev/null +++ b/internal/store/postgres/migrations/000054_job_run_constraint.down.sql @@ -0,0 +1 @@ +ALTER TABLE JOB_RUN DROP CONSTRAINT pk_constraint_job_name_scheduled_at; \ No newline at end of file diff --git a/internal/store/postgres/migrations/000054_job_run_constraint.up.sql b/internal/store/postgres/migrations/000054_job_run_constraint.up.sql new file mode 100644 index 0000000000..6e8a58a24b --- /dev/null +++ b/internal/store/postgres/migrations/000054_job_run_constraint.up.sql @@ -0,0 +1,10 @@ +DELETE FROM JOB_RUN +WHERE (JOB_NAME, SCHEDULED_AT) IN ( + SELECT JOB_NAME, SCHEDULED_AT + FROM JOB_RUN + GROUP BY JOB_NAME, SCHEDULED_AT + HAVING COUNT(*) > 1 +); + +ALTER TABLE JOB_RUN +ADD CONSTRAINT pk_constraint_job_name_scheduled_at UNIQUE (JOB_NAME, SCHEDULED_AT); \ No newline at end of file diff --git a/internal/store/postgres/migrations/000055_job_enable_status.down.sql b/internal/store/postgres/migrations/000055_job_enable_status.down.sql new file mode 100644 index 0000000000..ba08bbfe61 --- /dev/null +++ b/internal/store/postgres/migrations/000055_job_enable_status.down.sql @@ -0,0 +1,3 @@ +ALTER TABLE JOB DROP COLUMN IF EXISTS STATE, DROP COLUMN IF EXISTS REMARK; + +DROP TYPE IF EXISTS VALID_JOB_STATE ; \ No newline at end of file diff --git a/internal/store/postgres/migrations/000055_job_enable_status.up.sql b/internal/store/postgres/migrations/000055_job_enable_status.up.sql new file mode 100644 index 0000000000..11481b0714 --- /dev/null +++ b/internal/store/postgres/migrations/000055_job_enable_status.up.sql @@ -0,0 +1,7 @@ +DO $$ BEGIN + CREATE TYPE VALID_JOB_STATE AS ENUM ('enabled', 'disabled'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +ALTER TABLE JOB ADD COLUMN IF NOT EXISTS STATE VALID_JOB_STATE DEFAULT 'enabled', ADD COLUMN IF NOT EXISTS REMARK TEXT; \ No newline at end of file diff --git a/internal/store/postgres/pgx.go b/internal/store/postgres/pgx.go index 936f05cd40..e748f9739e 100644 --- a/internal/store/postgres/pgx.go +++ b/internal/store/postgres/pgx.go @@ -6,7 +6,7 @@ import ( "github.com/jackc/pgx/v5/pgxpool" - "github.com/odpf/optimus/config" + "github.com/raystack/optimus/config" ) // Open will connect to the DB with custom configuration diff --git a/internal/store/postgres/resource/backup_repository.go b/internal/store/postgres/resource/backup_repository.go index 3a75be6f1e..836fcdaa92 100644 --- a/internal/store/postgres/resource/backup_repository.go +++ b/internal/store/postgres/resource/backup_repository.go @@ -9,9 +9,9 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/lib/pq" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" ) const ( diff --git a/internal/store/postgres/resource/backup_repository_test.go b/internal/store/postgres/resource/backup_repository_test.go index 24ecdb7c11..118a674c55 100644 --- a/internal/store/postgres/resource/backup_repository_test.go +++ b/internal/store/postgres/resource/backup_repository_test.go @@ -9,9 +9,9 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/tenant" - postgres "github.com/odpf/optimus/internal/store/postgres/resource" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/tenant" + postgres "github.com/raystack/optimus/internal/store/postgres/resource" ) func TestPostgresBackupRepository(t *testing.T) { diff --git a/internal/store/postgres/resource/repository.go b/internal/store/postgres/resource/repository.go index 9c5ffe83d9..eb0f8c41cc 100644 --- a/internal/store/postgres/resource/repository.go +++ b/internal/store/postgres/resource/repository.go @@ -2,13 +2,14 @@ package resource import ( "context" + "fmt" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" ) const ( @@ -52,6 +53,22 @@ func (r Repository) Update(ctx context.Context, resourceModel *resource.Resource return nil } +func (r Repository) ChangeNamespace(ctx context.Context, res *resource.Resource, newTenant tenant.Tenant) error { + updateResource := `UPDATE resource SET namespace_name=$1, updated_at=now() + WHERE full_name=$2 AND store=$3 AND project_name = $4 And namespace_name = $5` + tag, err := r.db.Exec(ctx, updateResource, + newTenant.NamespaceName(), res.FullName(), res.Store(), + res.Tenant().ProjectName(), res.Tenant().NamespaceName()) + if err != nil { + return errors.Wrap(resource.EntityResource, "error changing tenant for resource:"+res.FullName(), err) + } + + if tag.RowsAffected() == 0 { + return errors.NotFound(resource.EntityResource, "no resource to changing tenant for ") + } + return nil +} + func (r Repository) ReadByFullName(ctx context.Context, tnnt tenant.Tenant, store resource.Store, fullName string) (*resource.Resource, error) { var res Resource getResource := `SELECT ` + resourceColumns + ` FROM resource WHERE full_name = $1 AND store = $2 AND @@ -61,10 +78,10 @@ func (r Repository) ReadByFullName(ctx context.Context, tnnt tenant.Tenant, stor &res.ProjectName, &res.NamespaceName, &res.Metadata, &res.Spec, &res.CreatedAt, &res.UpdatedAt) if err != nil { if errors.Is(err, pgx.ErrNoRows) { - return nil, errors.NotFound(resource.EntityResource, "no resource found for "+res.FullName) + return nil, errors.NotFound(resource.EntityResource, fmt.Sprintf("no resource: '%s' found for project:%s, namespace:%s ", fullName, tnnt.ProjectName(), tnnt.NamespaceName())) } - return nil, errors.Wrap(resource.EntityResource, "error reading the resource "+res.FullName, err) + return nil, errors.Wrap(resource.EntityResource, fmt.Sprintf("error reading resource: '%s' found for project:%s, namespace:%s ", fullName, tnnt.ProjectName(), tnnt.NamespaceName()), err) } return FromModelToResource(&res) @@ -144,5 +161,5 @@ func (r Repository) UpdateStatus(ctx context.Context, resources ...*resource.Res } } - return errors.MultiToError(multiErr) + return multiErr.ToErr() } diff --git a/internal/store/postgres/resource/repository_test.go b/internal/store/postgres/resource/repository_test.go index 8244b74f1b..92bb76c917 100644 --- a/internal/store/postgres/resource/repository_test.go +++ b/internal/store/postgres/resource/repository_test.go @@ -9,11 +9,11 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/stretchr/testify/assert" - serviceResource "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/tenant" - repoResource "github.com/odpf/optimus/internal/store/postgres/resource" - tenantPostgres "github.com/odpf/optimus/internal/store/postgres/tenant" - "github.com/odpf/optimus/tests/setup" + serviceResource "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/tenant" + repoResource "github.com/raystack/optimus/internal/store/postgres/resource" + tenantPostgres "github.com/raystack/optimus/internal/store/postgres/tenant" + "github.com/raystack/optimus/tests/setup" ) func TestPostgresResourceRepository(t *testing.T) { @@ -101,6 +101,46 @@ func TestPostgresResourceRepository(t *testing.T) { }) }) + t.Run("ChangeNamespace", func(t *testing.T) { + newNamespaceName := "n-optimus-2" + newTenant, err := tenant.NewTenant("t-optimus-1", newNamespaceName) + assert.Nil(t, err) + t.Run("returns error if resource does not exist", func(t *testing.T) { + pool := dbSetup() + repository := repoResource.NewRepository(pool) + + resourceToUpdate, err := serviceResource.NewResource("project.dataset", kindDataset, store, tnnt, meta, spec) + assert.NoError(t, err) + resourceToUpdate.UpdateURN("bigquery://project:dataset") + + actualError := repository.ChangeNamespace(ctx, resourceToUpdate, newTenant) + assert.ErrorContains(t, actualError, "not found for entity resource") + }) + + t.Run("updates resource and returns nil if no error is encountered", func(t *testing.T) { + pool := dbSetup() + repository := repoResource.NewRepository(pool) + + resourceToCreate, err := serviceResource.NewResource("project.dataset", kindDataset, store, tnnt, meta, spec) + assert.NoError(t, err) + resourceToCreate.UpdateURN("bigquery://project:dataset") + + err = repository.Create(ctx, resourceToCreate) + assert.NoError(t, err) + + resourceToUpdate := serviceResource.FromExisting(resourceToCreate, serviceResource.ReplaceStatus(serviceResource.StatusSuccess)) + actualError := repository.ChangeNamespace(ctx, resourceToUpdate, newTenant) + assert.NoError(t, actualError) + + storedResources, err := repository.GetResources(ctx, tnnt, store, []string{resourceToUpdate.FullName()}) + assert.NoError(t, err) + assert.Len(t, storedResources, 0) + storedNewResources, err := repository.GetResources(ctx, newTenant, store, []string{resourceToUpdate.FullName()}) + assert.NoError(t, err) + assert.Len(t, storedNewResources, 1) + }) + }) + t.Run("ReadByFullName", func(t *testing.T) { t.Run("returns nil and error if resource does not exist", func(t *testing.T) { pool := dbSetup() @@ -271,6 +311,7 @@ func dbSetup() *pgxpool.Pool { } namespaceRepo := tenantPostgres.NewNamespaceRepository(pool) + ns, _ := tenant.NewNamespace("n-optimus-1", proj.Name(), map[string]string{ "bucket": "gs://ns_bucket", @@ -280,5 +321,14 @@ func dbSetup() *pgxpool.Pool { panic(err) } + ns2, _ := tenant.NewNamespace("n-optimus-2", proj.Name(), + map[string]string{ + "bucket": "gs://ns_bucket", + }) + err = namespaceRepo.Save(ctx, ns2) + if err != nil { + panic(err) + } + return pool } diff --git a/internal/store/postgres/resource/resource.go b/internal/store/postgres/resource/resource.go index fbc0c296be..eac170c5ce 100644 --- a/internal/store/postgres/resource/resource.go +++ b/internal/store/postgres/resource/resource.go @@ -6,9 +6,9 @@ import ( "github.com/google/uuid" - "github.com/odpf/optimus/core/resource" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/resource" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" ) type Resource struct { diff --git a/internal/store/postgres/scheduler/job_operator_repository.go b/internal/store/postgres/scheduler/job_operator_repository.go index 40c6ec8091..ab301c6c07 100644 --- a/internal/store/postgres/scheduler/job_operator_repository.go +++ b/internal/store/postgres/scheduler/job_operator_repository.go @@ -9,8 +9,8 @@ import ( "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/internal/errors" ) const ( @@ -23,7 +23,6 @@ const ( ) type OperatorRunRepository struct { - // TODO: Add test db *pgxpool.Pool } diff --git a/internal/store/postgres/scheduler/job_operator_repository_test.go b/internal/store/postgres/scheduler/job_operator_repository_test.go index 9635f8033d..8b8787bc5b 100644 --- a/internal/store/postgres/scheduler/job_operator_repository_test.go +++ b/internal/store/postgres/scheduler/job_operator_repository_test.go @@ -9,10 +9,10 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" - postgres "github.com/odpf/optimus/internal/store/postgres/scheduler" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + postgres "github.com/raystack/optimus/internal/store/postgres/scheduler" ) func TestPostgresJobOperatorRepository(t *testing.T) { diff --git a/internal/store/postgres/scheduler/job_repository.go b/internal/store/postgres/scheduler/job_repository.go index 2e98df6fd3..b16dce6052 100644 --- a/internal/store/postgres/scheduler/job_repository.go +++ b/internal/store/postgres/scheduler/job_repository.go @@ -13,12 +13,12 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/lib/pq" - "github.com/odpf/optimus/core/job" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" - "github.com/odpf/optimus/internal/models" - "github.com/odpf/optimus/internal/utils" + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/models" + "github.com/raystack/optimus/internal/utils" ) const ( @@ -37,14 +37,13 @@ type Schedule struct { StartDate time.Time EndDate *time.Time Interval string - DependsOnPast bool `json:"depends_on_past"` - CatchUp bool `json:"catch_up"` + DependsOnPast bool Retry *Retry } type Retry struct { Count int `json:"count"` Delay int32 `json:"delay"` - ExponentialBackoff bool `json:"exponential_backoff"` + ExponentialBackoff bool } type JobUpstreams struct { @@ -139,6 +138,56 @@ func fromStorageWindow(raw []byte, jobVersion int) (models.Window, error) { ) } +type Metadata struct { + Resource *MetadataResource + Scheduler map[string]string +} + +type MetadataResource struct { + Request *MetadataResourceConfig + Limit *MetadataResourceConfig +} + +type MetadataResourceConfig struct { + CPU string + Memory string +} + +func fromStorageMetadata(metadata json.RawMessage) (scheduler.RuntimeConfig, error) { + if metadata == nil { + return scheduler.RuntimeConfig{}, nil + } + var storeMetadata Metadata + if err := json.Unmarshal(metadata, &storeMetadata); err != nil { + return scheduler.RuntimeConfig{}, err + } + var runtimeConfig scheduler.RuntimeConfig + if storeMetadata.Resource != nil { + var resourceRequest *scheduler.ResourceConfig + if storeMetadata.Resource.Request != nil { + resourceRequest = &scheduler.ResourceConfig{ + CPU: storeMetadata.Resource.Request.CPU, + Memory: storeMetadata.Resource.Request.Memory, + } + } + var resourceLimit *scheduler.ResourceConfig + if storeMetadata.Resource.Limit != nil { + resourceLimit = &scheduler.ResourceConfig{ + CPU: storeMetadata.Resource.Limit.CPU, + Memory: storeMetadata.Resource.Limit.Memory, + } + } + runtimeConfig.Resource = &scheduler.Resource{ + Request: resourceRequest, + Limit: resourceLimit, + } + } + if storeMetadata.Scheduler != nil { + runtimeConfig.Scheduler = storeMetadata.Scheduler + } + return runtimeConfig, nil +} + func (j *Job) toJob() (*scheduler.Job, error) { t, err := tenant.NewTenant(j.ProjectName, j.NamespaceName) if err != nil { @@ -184,6 +233,11 @@ func (j *Job) toJobWithDetails() (*scheduler.JobWithDetails, error) { return nil, err } + runtimeConfig, err := fromStorageMetadata(j.Metadata) + if err != nil { + return nil, err + } + schedulerJobWithDetails := &scheduler.JobWithDetails{ Name: job.Name, Job: job, @@ -195,10 +249,10 @@ func (j *Job) toJobWithDetails() (*scheduler.JobWithDetails, error) { }, Schedule: &scheduler.Schedule{ DependsOnPast: storageSchedule.DependsOnPast, - CatchUp: storageSchedule.CatchUp, StartDate: storageSchedule.StartDate, Interval: storageSchedule.Interval, }, + RuntimeConfig: runtimeConfig, } if !(storageSchedule.EndDate == nil || storageSchedule.EndDate.IsZero()) { schedulerJobWithDetails.Schedule.EndDate = storageSchedule.EndDate @@ -344,6 +398,48 @@ func (j *JobRepository) GetAll(ctx context.Context, projectName tenant.ProjectNa jobsMap[jobName].Upstreams.UpstreamJobs = upstreamList } + return utils.MapToList[*scheduler.JobWithDetails](jobsMap), multiError.ToErr() +} + +func (j *JobRepository) GetJobs(ctx context.Context, projectName tenant.ProjectName, jobs []string) ([]*scheduler.JobWithDetails, error) { + getJobByNames := `SELECT ` + jobColumns + ` FROM job WHERE project_name = $1 AND name = any ($2) AND deleted_at IS NULL` + rows, err := j.db.Query(ctx, getJobByNames, projectName, jobs) + if err != nil { + return nil, errors.Wrap(job.EntityJob, "error while getting selected jobs", err) + } + defer rows.Close() + + jobsMap := map[string]*scheduler.JobWithDetails{} + var jobNameList []string + multiError := errors.NewMultiError("errorInGetJobs") + for rows.Next() { + spec, err := FromRow(rows) + if err != nil { + multiError.Append(errors.Wrap(scheduler.EntityJobRun, "error parsing job:"+spec.Name, err)) + continue + } + + job, err := spec.toJobWithDetails() + if err != nil { + multiError.Append(errors.Wrap(scheduler.EntityJobRun, "error parsing job:"+spec.Name, err)) + continue + } + jobNameList = append(jobNameList, job.GetName()) + jobsMap[job.GetName()] = job + } + for _, jobName := range jobs { + if _, ok := jobsMap[jobName]; !ok { + multiError.Append(errors.NotFound(scheduler.EntityJobRun, "unable to find job "+jobName)) + } + } + + jobUpstreamGroupedByName, err := j.getJobsUpstreams(ctx, projectName, jobNameList) + multiError.Append(err) + + for jobName, upstreamList := range jobUpstreamGroupedByName { + jobsMap[jobName].Upstreams.UpstreamJobs = upstreamList + } + return utils.MapToList[*scheduler.JobWithDetails](jobsMap), errors.MultiToError(multiError) } diff --git a/internal/store/postgres/scheduler/job_repository_test.go b/internal/store/postgres/scheduler/job_repository_test.go index 572833633b..e579427668 100644 --- a/internal/store/postgres/scheduler/job_repository_test.go +++ b/internal/store/postgres/scheduler/job_repository_test.go @@ -4,20 +4,21 @@ package scheduler_test import ( "context" + "reflect" "testing" "github.com/jackc/pgx/v5/pgxpool" "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/job" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" - "github.com/odpf/optimus/internal/models" - jobRepo "github.com/odpf/optimus/internal/store/postgres/job" - postgres "github.com/odpf/optimus/internal/store/postgres/scheduler" - tenantPostgres "github.com/odpf/optimus/internal/store/postgres/tenant" - "github.com/odpf/optimus/tests/setup" + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/models" + jobRepo "github.com/raystack/optimus/internal/store/postgres/job" + postgres "github.com/raystack/optimus/internal/store/postgres/scheduler" + tenantPostgres "github.com/raystack/optimus/internal/store/postgres/tenant" + "github.com/raystack/optimus/tests/setup" ) const ( @@ -108,6 +109,38 @@ func TestPostgresJobRepository(t *testing.T) { assert.Nil(t, jobObject) }) }) + t.Run("GetJobs", func(t *testing.T) { + t.Run("returns multiple job", func(t *testing.T) { + db := dbSetup() + jobs := addJobs(ctx, t, db) + jobProviderRepo := postgres.NewJobProviderRepository(db) + + jobObjects, err := jobProviderRepo.GetJobs(ctx, tnnt.ProjectName(), []string{jobAName, jobBName}) + assert.Nil(t, err) + assert.Equal(t, 2, len(jobObjects)) + for _, jobObject := range jobObjects { + compareEqualJobWithDetails(jobs[jobObject.Name.String()], jobObject) + } + }) + t.Run("returns not found error when jobs are not found", func(t *testing.T) { + db := dbSetup() + jobProviderRepo := postgres.NewJobProviderRepository(db) + jobObject, err := jobProviderRepo.GetJobs(ctx, tnnt.ProjectName(), []string{"some-other-job"}) + assert.ErrorContains(t, err, "unable to find job") + assert.Nil(t, jobObject) + }) + t.Run("returns the found job when some other job is not found", func(t *testing.T) { + db := dbSetup() + jobs := addJobs(ctx, t, db) + jobProviderRepo := postgres.NewJobProviderRepository(db) + jobObjects, err := jobProviderRepo.GetJobs(ctx, tnnt.ProjectName(), []string{jobAName, "some-other-job"}) + assert.ErrorContains(t, err, "unable to find job") + assert.Equal(t, 1, len(jobObjects)) + for _, jobObject := range jobObjects { + compareEqualJobWithDetails(jobs[jobObject.Name.String()], jobObject) + } + }) + }) } func dbSetup() *pgxpool.Pool { @@ -121,10 +154,10 @@ func addJobs(ctx context.Context, t *testing.T, pool *pgxpool.Pool) map[string]* jobVersion := 1 jobOwner := "dev_test" jobDescription := "sample job" - jobRetry := job.NewRetry(5, 0, false) + jobRetry := job.NewRetry(5, 0, true) startDate, err := job.ScheduleDateFrom("2022-10-01") assert.NoError(t, err) - jobSchedule, err := job.NewScheduleBuilder(startDate).WithRetry(jobRetry).Build() + jobSchedule, err := job.NewScheduleBuilder(startDate).WithRetry(jobRetry).WithDependsOnPast(true).Build() assert.NoError(t, err) jobWindow, err := models.NewWindow(jobVersion, "d", "24h", "24h") assert.NoError(t, err) @@ -236,5 +269,8 @@ func compareEqualJobWithDetails(j *job.Job, s *scheduler.JobWithDetails) bool { j.GetName() == s.Name.String() && j.Spec().Version() == s.JobMetadata.Version && j.Spec().Owner() == s.JobMetadata.Owner && - j.Spec().Schedule().Interval() == s.Schedule.Interval + j.Spec().Schedule().Interval() == s.Schedule.Interval && + j.Spec().Schedule().DependsOnPast() == s.Schedule.DependsOnPast && + j.Spec().Schedule().Retry().ExponentialBackoff() == s.Retry.ExponentialBackoff && + reflect.DeepEqual(j.Spec().Metadata().Scheduler(), s.RuntimeConfig.Scheduler) } diff --git a/internal/store/postgres/scheduler/job_run_repository.go b/internal/store/postgres/scheduler/job_run_repository.go index 62d2879c5a..e772f8541a 100644 --- a/internal/store/postgres/scheduler/job_run_repository.go +++ b/internal/store/postgres/scheduler/job_run_repository.go @@ -10,9 +10,9 @@ import ( "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" ) const ( @@ -61,14 +61,15 @@ func (j *jobRun) toJobRun() (*scheduler.JobRun, error) { } } return &scheduler.JobRun{ - ID: j.ID, - JobName: scheduler.JobName(j.JobName), - Tenant: t, - State: state, - StartTime: j.StartTime, - SLAAlert: j.SLAAlert, - EndTime: j.EndTime, - Monitoring: monitoring, + ID: j.ID, + JobName: scheduler.JobName(j.JobName), + Tenant: t, + State: state, + ScheduledAt: j.ScheduledAt, + StartTime: j.StartTime, + SLAAlert: j.SLAAlert, + EndTime: j.EndTime, + Monitoring: monitoring, }, nil } @@ -79,7 +80,10 @@ func (j *JobRunRepository) GetByID(ctx context.Context, id scheduler.JobRunID) ( Scan(&jr.ID, &jr.JobName, &jr.NamespaceName, &jr.ProjectName, &jr.ScheduledAt, &jr.StartTime, &jr.EndTime, &jr.Status, &jr.SLADefinition, &jr.SLAAlert, &jr.Monitoring) if err != nil { - return nil, err + if errors.Is(err, pgx.ErrNoRows) { + return nil, errors.NotFound(scheduler.EntityJobRun, "no record for job run id "+id.UUID().String()) + } + return nil, errors.Wrap(scheduler.EntityJobRun, "error while getting job run", err) } return jr.toJobRun() } @@ -99,6 +103,12 @@ func (j *JobRunRepository) GetByScheduledAt(ctx context.Context, t tenant.Tenant return jr.toJobRun() } +func (j *JobRunRepository) UpdateState(ctx context.Context, jobRunID uuid.UUID, status scheduler.State) error { + updateJobRun := "update job_run set status = $1, updated_at = NOW() where id = $2" + _, err := j.db.Exec(ctx, updateJobRun, status, jobRunID) + return errors.WrapIfErr(scheduler.EntityJobRun, "unable to update job run", err) +} + func (j *JobRunRepository) Update(ctx context.Context, jobRunID uuid.UUID, endTime time.Time, status scheduler.State) error { updateJobRun := "update job_run set status = $1, end_time = $2, updated_at = NOW() where id = $3" _, err := j.db.Exec(ctx, updateJobRun, status, endTime, jobRunID) @@ -130,7 +140,7 @@ func (j *JobRunRepository) UpdateMonitoring(ctx context.Context, jobRunID uuid.U } func (j *JobRunRepository) Create(ctx context.Context, t tenant.Tenant, jobName scheduler.JobName, scheduledAt time.Time, slaDefinitionInSec int64) error { - insertJobRun := `INSERT INTO job_run (` + columnsToStore + `, created_at, updated_at) values ($1, $2, $3, $4, NOW(), TIMESTAMP '3000-01-01 00:00:00', $5, $6, FALSE, NOW(), NOW())` + insertJobRun := `INSERT INTO job_run (` + columnsToStore + `, created_at, updated_at) values ($1, $2, $3, $4, NOW(), TIMESTAMP '3000-01-01 00:00:00', $5, $6, FALSE, NOW(), NOW()) ON CONFLICT DO NOTHING` _, err := j.db.Exec(ctx, insertJobRun, jobName, t.NamespaceName(), t.ProjectName(), scheduledAt, scheduler.StateRunning, slaDefinitionInSec) return errors.WrapIfErr(scheduler.EntityJobRun, "unable to create job run", err) } diff --git a/internal/store/postgres/scheduler/job_run_repository_test.go b/internal/store/postgres/scheduler/job_run_repository_test.go index e9d5621c7c..c21b119247 100644 --- a/internal/store/postgres/scheduler/job_run_repository_test.go +++ b/internal/store/postgres/scheduler/job_run_repository_test.go @@ -9,9 +9,9 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/tenant" - postgres "github.com/odpf/optimus/internal/store/postgres/scheduler" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/tenant" + postgres "github.com/raystack/optimus/internal/store/postgres/scheduler" ) func TestPostgresJobRunRepository(t *testing.T) { diff --git a/internal/store/postgres/scheduler/replay_repository.go b/internal/store/postgres/scheduler/replay_repository.go index 764e7fba5e..7ca3ec1d8b 100644 --- a/internal/store/postgres/scheduler/replay_repository.go +++ b/internal/store/postgres/scheduler/replay_repository.go @@ -9,10 +9,10 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "golang.org/x/net/context" - "github.com/odpf/optimus/core/job" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" ) const ( @@ -205,6 +205,71 @@ func (r ReplayRepository) GetReplayRequestsByStatus(ctx context.Context, statusL return replayReqs, nil } +func (r ReplayRepository) GetReplaysByProject(ctx context.Context, projectName tenant.ProjectName, dayLimits int) ([]*scheduler.Replay, error) { + getReplayRequest := `SELECT ` + replayColumns + ` FROM replay_request WHERE project_name=$1 LIMIT $2` + rows, err := r.db.Query(ctx, getReplayRequest, projectName, dayLimits) + if err != nil { + return nil, errors.Wrap(job.EntityJob, "unable to get replay list", err) + } + defer rows.Close() + + var replayReqs []*scheduler.Replay + for rows.Next() { + var rr replayRequest + if err := rows.Scan(&rr.ID, &rr.JobName, &rr.NamespaceName, &rr.ProjectName, &rr.StartTime, &rr.EndTime, &rr.Description, &rr.Parallel, &rr.JobConfig, + &rr.Status, &rr.Message, &rr.CreatedAt); err != nil { + return nil, errors.Wrap(scheduler.EntityJobRun, "unable to get the stored replay", err) + } + schedulerReplayReq, err := rr.toSchedulerReplayRequest() + if err != nil { + return nil, err + } + replayReqs = append(replayReqs, schedulerReplayReq) + } + return replayReqs, nil +} + +func (r ReplayRepository) GetReplayByID(ctx context.Context, replayID uuid.UUID) (*scheduler.ReplayWithRun, error) { + rr, err := r.getReplayRequestByID(ctx, replayID) + if err != nil { + if !errors.Is(err, pgx.ErrNoRows) { + return nil, err + } + return nil, errors.NotFound(job.EntityJob, fmt.Sprintf("no replay found for replay ID %s", replayID.String())) + } + + runs, err := r.getReplayRuns(ctx, replayID) + if err != nil && !errors.Is(err, pgx.ErrNoRows) { + return nil, err + } + + replayTenant, err := tenant.NewTenant(rr.ProjectName, rr.NamespaceName) + if err != nil { + return nil, err + } + replayConfig := scheduler.ReplayConfig{ + StartTime: rr.StartTime, + EndTime: rr.EndTime, + JobConfig: rr.JobConfig, + Parallel: rr.Parallel, + Description: rr.Description, + } + replay := scheduler.NewReplay(rr.ID, scheduler.JobName(rr.JobName), replayTenant, &replayConfig, scheduler.ReplayState(rr.Status), rr.CreatedAt) + replayRuns := make([]*scheduler.JobRunStatus, len(runs)) + for i := range runs { + replayRun := &scheduler.JobRunStatus{ + ScheduledAt: runs[i].ScheduledTime, + State: scheduler.State(runs[i].RunStatus), + } + replayRuns[i] = replayRun + } + + return &scheduler.ReplayWithRun{ + Replay: replay, + Runs: replayRuns, + }, nil +} + func toReplay(replayRuns []*replayRun) (*scheduler.ReplayWithRun, error) { var storedReplay *scheduler.ReplayWithRun for _, run := range replayRuns { @@ -323,6 +388,35 @@ func (ReplayRepository) getReplayRequest(ctx context.Context, tx pgx.Tx, replay return rr, nil } +func (r ReplayRepository) getReplayRequestByID(ctx context.Context, replayID uuid.UUID) (replayRequest, error) { + var rr replayRequest + getReplayRequest := `SELECT ` + replayColumns + ` FROM replay_request WHERE id=$1` + err := r.db.QueryRow(ctx, getReplayRequest, replayID).Scan(&rr.ID, &rr.JobName, &rr.NamespaceName, &rr.ProjectName, &rr.StartTime, &rr.EndTime, &rr.Description, &rr.Parallel, &rr.JobConfig, + &rr.Status, &rr.Message, &rr.CreatedAt) + if err != nil { + return rr, err + } + return rr, nil +} + +func (r ReplayRepository) getReplayRuns(ctx context.Context, replayID uuid.UUID) ([]replayRun, error) { + var runs []replayRun + getRuns := `SELECT ` + replayRunColumns + ` FROM replay_run WHERE replay_id=$1` + rows, err := r.db.Query(ctx, getRuns, replayID) + if err != nil { + return nil, err + } + for rows.Next() { + var run replayRun + err = rows.Scan(&run.ID, &run.ScheduledTime, &run.RunStatus) + if err != nil { + return nil, err + } + runs = append(runs, run) + } + return runs, nil +} + func (ReplayRepository) getExecutableReplayRuns(ctx context.Context, tx pgx.Tx) ([]*replayRun, error) { getReplayRequest := ` WITH request AS ( diff --git a/internal/store/postgres/scheduler/replay_repository_test.go b/internal/store/postgres/scheduler/replay_repository_test.go index d65ccc1fe5..f9c897f0eb 100644 --- a/internal/store/postgres/scheduler/replay_repository_test.go +++ b/internal/store/postgres/scheduler/replay_repository_test.go @@ -7,11 +7,13 @@ import ( "testing" "time" + "github.com/google/uuid" "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/scheduler" - "github.com/odpf/optimus/core/tenant" - postgres "github.com/odpf/optimus/internal/store/postgres/scheduler" + "github.com/raystack/optimus/core/scheduler" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" + postgres "github.com/raystack/optimus/internal/store/postgres/scheduler" ) func TestPostgresSchedulerRepository(t *testing.T) { @@ -140,6 +142,47 @@ func TestPostgresSchedulerRepository(t *testing.T) { }) }) + t.Run("GetReplaysByProject", func(t *testing.T) { + t.Run("return replay list for corresponding project name", func(t *testing.T) { + db := dbSetup() + replayRepo := postgres.NewReplayRepository(db) + tnnt, _ := tenant.NewTenant("test-project1", "ns-1") + tnntOther, _ := tenant.NewTenant("test-project2", "ns-1") + + replayConfig := scheduler.NewReplayConfig(startTime, endTime, true, replayJobConfig, description) + replayReq1 := scheduler.NewReplayRequest(jobAName, tnnt, replayConfig, scheduler.ReplayStateInProgress) + replayReq2 := scheduler.NewReplayRequest(jobBName, tnnt, replayConfig, scheduler.ReplayStateCreated) + replayReq3 := scheduler.NewReplayRequest("sample-job-C", tnntOther, replayConfig, scheduler.ReplayStateFailed) + + replayID1, err := replayRepo.RegisterReplay(ctx, replayReq1, jobRunsAllPending) + assert.Nil(t, err) + assert.NotNil(t, replayID1) + + replayID2, err := replayRepo.RegisterReplay(ctx, replayReq2, jobRunsAllPending) + assert.Nil(t, err) + assert.NotNil(t, replayID2) + + replayID3, err := replayRepo.RegisterReplay(ctx, replayReq3, jobRunsAllPending) + assert.Nil(t, err) + assert.NotNil(t, replayID3) + + replayReqs, err := replayRepo.GetReplaysByProject(ctx, tnnt.ProjectName(), 3) + assert.Nil(t, err) + assert.Len(t, replayReqs, 2) + + replayReqs, err = replayRepo.GetReplaysByProject(ctx, tnntOther.ProjectName(), 3) + assert.Nil(t, err) + assert.Len(t, replayReqs, 1) + }) + t.Run("return empty list when replay is not existed on given project", func(t *testing.T) { + db := dbSetup() + replayRepo := postgres.NewReplayRepository(db) + replayReqs, err := replayRepo.GetReplaysByProject(ctx, tnnt.ProjectName(), 3) + assert.Nil(t, err) + assert.Len(t, replayReqs, 0) + }) + }) + t.Run("GetReplayJobConfig", func(t *testing.T) { t.Run("return replay task config when scheduledAt is provided", func(t *testing.T) { db := dbSetup() @@ -178,4 +221,51 @@ func TestPostgresSchedulerRepository(t *testing.T) { assert.Equal(t, map[string]string{}, actualReplayJobConfig) }) }) + + t.Run("GetReplayByID", func(t *testing.T) { + t.Run("return no replay with runs if not exist", func(t *testing.T) { + db := dbSetup() + replayRepo := postgres.NewReplayRepository(db) + + replayID := uuid.New() + replayWithRuns, err := replayRepo.GetReplayByID(ctx, replayID) + assert.NotNil(t, err) + assert.True(t, errors.IsErrorType(err, errors.ErrNotFound)) + assert.Empty(t, replayWithRuns) + }) + + t.Run("return replay with no runs if runs is empty", func(t *testing.T) { + db := dbSetup() + replayRepo := postgres.NewReplayRepository(db) + startTime, _ := time.Parse(scheduler.ISODateFormat, "2022-01-01T15:04:05Z") + endTime, _ := time.Parse(scheduler.ISODateFormat, "2022-01-03T15:04:05Z") + + replayConfig := scheduler.NewReplayConfig(startTime, endTime, true, map[string]string{}, description) + replayReq := scheduler.NewReplayRequest(jobBName, tnnt, replayConfig, scheduler.ReplayStateCreated) + replayID, err := replayRepo.RegisterReplay(ctx, replayReq, []*scheduler.JobRunStatus{}) + assert.Nil(t, err) + + replayWithRuns, err := replayRepo.GetReplayByID(ctx, replayID) + assert.Nil(t, err) + assert.NotEmpty(t, replayWithRuns) + assert.Empty(t, replayWithRuns.Runs) + }) + t.Run("return replay with runs given replay ID", func(t *testing.T) { + db := dbSetup() + replayRepo := postgres.NewReplayRepository(db) + startTime, _ := time.Parse(scheduler.ISODateFormat, "2022-01-01T15:04:05Z") + endTime, _ := time.Parse(scheduler.ISODateFormat, "2022-01-03T15:04:05Z") + + replayConfig := scheduler.NewReplayConfig(startTime, endTime, true, map[string]string{}, description) + replayReq := scheduler.NewReplayRequest(jobBName, tnnt, replayConfig, scheduler.ReplayStateCreated) + replayID, err := replayRepo.RegisterReplay(ctx, replayReq, jobRunsAllPending) + assert.Nil(t, err) + + replayWithRuns, err := replayRepo.GetReplayByID(ctx, replayID) + assert.Nil(t, err) + assert.NotEmpty(t, replayWithRuns) + assert.NotEmpty(t, replayWithRuns.Runs) + assert.Len(t, replayWithRuns.Runs, len(jobRunsAllPending)) + }) + }) } diff --git a/internal/store/postgres/tenant/namespace_repository.go b/internal/store/postgres/tenant/namespace_repository.go index a66ee04254..2fb58fbdbb 100644 --- a/internal/store/postgres/tenant/namespace_repository.go +++ b/internal/store/postgres/tenant/namespace_repository.go @@ -8,8 +8,8 @@ import ( "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" ) type NamespaceRepository struct { diff --git a/internal/store/postgres/tenant/namespace_repository_test.go b/internal/store/postgres/tenant/namespace_repository_test.go index b27f50dc21..8a1e3f75b7 100644 --- a/internal/store/postgres/tenant/namespace_repository_test.go +++ b/internal/store/postgres/tenant/namespace_repository_test.go @@ -9,9 +9,9 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/tenant" - postgres "github.com/odpf/optimus/internal/store/postgres/tenant" - "github.com/odpf/optimus/tests/setup" + "github.com/raystack/optimus/core/tenant" + postgres "github.com/raystack/optimus/internal/store/postgres/tenant" + "github.com/raystack/optimus/tests/setup" ) func TestPostgresNamespaceRepository(t *testing.T) { diff --git a/internal/store/postgres/tenant/project_repository.go b/internal/store/postgres/tenant/project_repository.go index 29aad58673..9f4203e64c 100644 --- a/internal/store/postgres/tenant/project_repository.go +++ b/internal/store/postgres/tenant/project_repository.go @@ -8,8 +8,8 @@ import ( "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/errors" ) type ProjectRepository struct { diff --git a/internal/store/postgres/tenant/project_repository_test.go b/internal/store/postgres/tenant/project_repository_test.go index c2804eff01..eef1b3d520 100644 --- a/internal/store/postgres/tenant/project_repository_test.go +++ b/internal/store/postgres/tenant/project_repository_test.go @@ -9,9 +9,9 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/tenant" - postgres "github.com/odpf/optimus/internal/store/postgres/tenant" - "github.com/odpf/optimus/tests/setup" + "github.com/raystack/optimus/core/tenant" + postgres "github.com/raystack/optimus/internal/store/postgres/tenant" + "github.com/raystack/optimus/tests/setup" ) func TestPostgresProjectRepository(t *testing.T) { diff --git a/internal/store/postgres/tenant/secret_repository.go b/internal/store/postgres/tenant/secret_repository.go index 7692ba2cdd..00573b0cd0 100644 --- a/internal/store/postgres/tenant/secret_repository.go +++ b/internal/store/postgres/tenant/secret_repository.go @@ -12,9 +12,9 @@ import ( "github.com/jackc/pgx/v5/pgconn" "github.com/jackc/pgx/v5/pgxpool" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/core/tenant/dto" - "github.com/odpf/optimus/internal/errors" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/core/tenant/dto" + "github.com/raystack/optimus/internal/errors" ) type SecretRepository struct { diff --git a/internal/store/postgres/tenant/secret_repository_test.go b/internal/store/postgres/tenant/secret_repository_test.go index 34706c00e9..4ac4f68c8d 100644 --- a/internal/store/postgres/tenant/secret_repository_test.go +++ b/internal/store/postgres/tenant/secret_repository_test.go @@ -9,9 +9,9 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/core/tenant" - postgres "github.com/odpf/optimus/internal/store/postgres/tenant" - "github.com/odpf/optimus/tests/setup" + "github.com/raystack/optimus/core/tenant" + postgres "github.com/raystack/optimus/internal/store/postgres/tenant" + "github.com/raystack/optimus/tests/setup" ) func TestPostgresSecretRepository(t *testing.T) { diff --git a/internal/telemetry/dashboards/Readme.md b/internal/telemetry/dashboards/Readme.md new file mode 100644 index 0000000000..92e9b9cc0e --- /dev/null +++ b/internal/telemetry/dashboards/Readme.md @@ -0,0 +1,3 @@ +To create a limited access role and user create a new role via ./scripts/add_monitoring_role.sql + +NOTE: these are not migration scripts, they are supposed to be used manualy if need be \ No newline at end of file diff --git a/internal/telemetry/dashboards/grafana8.json b/internal/telemetry/dashboards/grafana8.json deleted file mode 100644 index 872a565f62..0000000000 --- a/internal/telemetry/dashboards/grafana8.json +++ /dev/null @@ -1,1285 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - } - ] - }, - "editable": true, - "gnetId": null, - "graphTooltip": 0, - "id": 326, - "iteration": 1663310204681, - "links": [], - "panels": [ - { - "collapsed": true, - "datasource": null, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 15, - "panels": [], - "title": "Overview", - "type": "row" - }, - { - "datasource": "Optimus-Server-DB", - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "light-purple", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 6, - "x": 2, - "y": 1 - }, - "id": 19, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "center", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "/^count$/", - "values": false - }, - "text": {}, - "textMode": "auto" - }, - "pluginVersion": "8.0.7", - "targets": [ - { - "format": "table", - "group": [], - "hide": false, - "metricColumn": "none", - "rawQuery": true, - "rawSql": "SELECT\n count(*)\nFROM\n project\n", - "refId": "A", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "column" - } - ] - ], - "timeColumn": "time", - "where": [ - { - "name": "$__timeFilter", - "params": [], - "type": "macro" - } - ] - } - ], - "title": "Projects", - "transparent": true, - "type": "stat" - }, - { - "datasource": "Optimus-Server-DB", - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "orange", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 6, - "x": 9, - "y": 1 - }, - "id": 21, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "center", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "/^count$/", - "values": false - }, - "text": {}, - "textMode": "value" - }, - "pluginVersion": "8.0.7", - "targets": [ - { - "format": "table", - "group": [], - "hide": false, - "metricColumn": "none", - "rawQuery": true, - "rawSql": "SELECT\n count(*)\nFROM\n namespace", - "refId": "A", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "column" - } - ] - ], - "timeColumn": "time", - "where": [ - { - "name": "$__timeFilter", - "params": [], - "type": "macro" - } - ] - } - ], - "title": "Namespaces", - "transparent": true, - "type": "stat" - }, - { - "datasource": "Optimus-Server-DB", - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "blue", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 6, - "x": 16, - "y": 1 - }, - "id": 23, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "center", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "/^count$/", - "values": false - }, - "text": {}, - "textMode": "auto" - }, - "pluginVersion": "8.0.7", - "targets": [ - { - "datasource": "Optimus-Server-DB", - "format": "table", - "group": [], - "metricColumn": "none", - "rawQuery": true, - "rawSql": "SELECT\n count(*)\nFROM\n job\nwhere deleted_at is null", - "refId": "A", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "column" - } - ] - ], - "timeColumn": "time", - "where": [ - { - "name": "$__timeFilter", - "params": [], - "type": "macro" - } - ] - } - ], - "title": "Jobs", - "transparent": true, - "type": "stat" - }, - { - "datasource": "Optimus-Server-DB", - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "orange", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 6 - }, - "id": 11, - "interval": "30m", - "options": { - "displayMode": "basic", - "minVizHeight": 10, - "minVizWidth": 1, - "orientation": "vertical", - "reduceOptions": { - "calcs": [], - "fields": "/^count$/", - "values": true - }, - "showUnfilled": true, - "text": {} - }, - "pluginVersion": "8.0.7", - "targets": [ - { - "datasource": "Optimus-Server-DB", - "format": "table", - "group": [], - "metricColumn": "none", - "rawQuery": true, - "rawSql": "SELECT\n p.name,\n count(*)\nFROM\n namespace n\nJOIN project p ON p.id = n.project_id\nGROUP BY p.name\nORDER BY 1", - "refId": "A", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "column" - } - ] - ], - "timeColumn": "time", - "where": [ - { - "name": "$__timeFilter", - "params": [], - "type": "macro" - } - ] - } - ], - "title": "Namespaces", - "type": "bargauge" - }, - { - "datasource": "Optimus-Server-DB", - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "light-red", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 6 - }, - "id": 9, - "interval": "30m", - "options": { - "displayMode": "basic", - "minVizHeight": 10, - "minVizWidth": 1, - "orientation": "vertical", - "reduceOptions": { - "calcs": [], - "fields": "/^count$/", - "values": true - }, - "showUnfilled": true, - "text": {} - }, - "pluginVersion": "8.0.7", - "targets": [ - { - "datasource": "Optimus-Server-DB", - "format": "table", - "group": [], - "metricColumn": "none", - "rawQuery": true, - "rawSql": "SELECT\n p.name,\n count(*)\nFROM\n secret s\nJOIN project p ON p.id = s.project_id\nGROUP BY p.name\nORDER BY 1", - "refId": "A", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "column" - } - ] - ], - "timeColumn": "time", - "where": [ - { - "name": "$__timeFilter", - "params": [], - "type": "macro" - } - ] - } - ], - "title": "Secrets", - "type": "bargauge" - }, - { - "datasource": "Optimus-Server-DB", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "displayMode": "auto" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "job_count" - }, - "properties": [ - { - "id": "custom.width", - "value": 236 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "depencecy_count" - }, - "properties": [ - { - "id": "custom.width", - "value": 199 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "dependency_count" - }, - "properties": [ - { - "id": "custom.width", - "value": 285 - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 8, - "x": 0, - "y": 14 - }, - "id": 27, - "options": { - "footer": { - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true, - "sortBy": [] - }, - "pluginVersion": "8.0.7", - "targets": [ - { - "datasource": "Optimus-Server-DB", - "format": "table", - "group": [], - "metricColumn": "none", - "rawQuery": true, - "rawSql": "SELECT cardinality as dependency_count, count(*) as job_count FROM\n(\n SELECT name, cardinality(ARRAY(SELECT jsonb_object_keys(dependencies)))\n FROM job\n WHERE deleted_at is NULL\n) as k\nGROUP by cardinality\norder by job_count desc", - "refId": "A", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "column" - } - ] - ], - "timeColumn": "time", - "where": [ - { - "name": "$__timeFilter", - "params": [], - "type": "macro" - } - ] - } - ], - "title": "Number jobs by dependency count", - "type": "table" - }, - { - "datasource": "Optimus-Server-DB", - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "displayMode": "auto" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "name" - }, - "properties": [ - { - "id": "custom.width", - "value": 183 - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 9, - "y": 14 - }, - "id": 29, - "options": { - "footer": { - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true, - "sortBy": [] - }, - "pluginVersion": "8.0.7", - "targets": [ - { - "datasource": "Optimus-Server-DB", - "format": "table", - "group": [], - "metricColumn": "none", - "rawQuery": true, - "rawSql": "SELECT\n\tn.name,\n\tcount(*)\nFROM\n\tjob j\nJOIN namespace n ON n.id = j.namespace_id\nwhere j.project_id = '$project'\nAND j.deleted_at is NULL\ngroup by n.name", - "refId": "A", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "column" - } - ] - ], - "timeColumn": "time", - "where": [ - { - "name": "$__timeFilter", - "params": [], - "type": "macro" - } - ] - } - ], - "title": "Jobs / Namespace in $project", - "type": "table" - }, - { - "datasource": "Optimus-Server-DB", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "displayMode": "auto" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 8, - "x": 16, - "y": 14 - }, - "id": 25, - "options": { - "footer": { - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true - }, - "pluginVersion": "8.0.7", - "targets": [ - { - "datasource": "Optimus-Server-DB", - "format": "table", - "group": [], - "metricColumn": "none", - "rawQuery": true, - "rawSql": "SELECT \n task_name as task_type,\n count(*) as jobs_count \nFROM \n job\nWHERE deleted_at is NULL\ngroup by task_name\norder by jobs_count desc\n", - "refId": "A", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "column" - } - ] - ], - "timeColumn": "time", - "where": [ - { - "name": "$__timeFilter", - "params": [], - "type": "macro" - } - ] - } - ], - "title": "jobs count per task type", - "type": "table" - }, - { - "collapsed": false, - "datasource": null, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 22 - }, - "id": 13, - "panels": [], - "title": "SLA Dashboard", - "type": "row" - }, - { - "datasource": "Optimus-Server-DB", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "displayMode": "auto" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 24, - "x": 0, - "y": 23 - }, - "id": 4, - "options": { - "footer": { - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true, - "sortBy": [ - { - "desc": false, - "displayName": "total_run_in_interval" - } - ] - }, - "pluginVersion": "8.0.7", - "targets": [ - { - "datasource": "Optimus-Server-DB", - "format": "table", - "group": [], - "metricColumn": "none", - "rawQuery": true, - "rawSql": "select \n\tfiltered.job_id, job.name,\n\tROUND((filtered.count*100)/total.count::decimal, 2) as miss_percentage ,\n\tfiltered.max_delay , filtered.avg_delay, total.count as total_runs_in_interval\nfrom (\n\tselect \n\t\tcount(*) as count , \n\t\tjob_id , \n\t\tmax(duration) as max_delay, \n\t\tavg(duration) as avg_delay \n\tfrom job_run \n\twhere $__timeFilter(scheduled_at)\n\t\tAND EXTRACT(EPOCH from least(end_time , now())- start_time) > sla_definition \t\t\n AND namespace_id = '$namespace' \n AND project_id = '$project'\n\tgroup by job_id\n) as filtered \ninner join (\n\tselect count(*) , job_id \n\tfrom job_run \n\twhere $__timeFilter(scheduled_at) \n AND namespace_id = '$namespace' \n AND project_id = '$project'\n\tgroup by job_id\n) as total on total.job_id = filtered.job_id\ninner join job on job.id = filtered.job_id", - "refId": "A", - "select": [ - [ - { - "params": [ - "duration" - ], - "type": "column" - } - ] - ], - "table": "job_run", - "timeColumn": "scheduled_at", - "timeColumnType": "timestamptz", - "where": [ - { - "name": "$__timeFilter", - "params": [], - "type": "macro" - }, - { - "datatype": "uuid", - "name": "", - "params": [ - "namespace_id", - "=", - "'$namespace'" - ], - "type": "expression" - }, - { - "datatype": "uuid", - "name": "", - "params": [ - "project_id", - "=", - "'$project'" - ], - "type": "expression" - } - ] - } - ], - "title": "SLA miss summary", - "type": "table" - }, - { - "datasource": "Optimus-Server-DB", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "displayMode": "auto" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 24, - "x": 0, - "y": 29 - }, - "id": 5, - "options": { - "footer": { - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true, - "sortBy": [ - { - "desc": false, - "displayName": "total_run_in_interval" - } - ] - }, - "pluginVersion": "8.0.7", - "targets": [ - { - "datasource": "Optimus-Server-DB", - "format": "table", - "group": [], - "metricColumn": "none", - "rawQuery": true, - "rawSql": "select job.name, \n\t\tlast_job_runs.scheduled_at,\n\t\tlast_job_runs.start_time , \n\t\tlast_job_runs.end_time, \n\t\tEXTRACT(EPOCH from least(last_job_runs.end_time , now())- last_job_runs.start_time) as total_job_run_time, \n\t\tlast_job_runs.sla_definition \nfrom \n\t(SELECT DISTINCT ON (job_id,scheduled_at ) * FROM job_run where $__timeFilter(scheduled_at) and namespace_id = '$namespace' \n AND project_id = '$project' ORDER BY job_id,scheduled_at, attempt DESC ) as last_job_runs\ninner join job \n\ton last_job_runs.job_id = job.id\n\twhere EXTRACT(EPOCH from least(last_job_runs.end_time , now())- last_job_runs.start_time) > last_job_runs.sla_definition \n", - "refId": "A", - "select": [ - [ - { - "params": [ - "duration" - ], - "type": "column" - } - ] - ], - "table": "job_run", - "timeColumn": "scheduled_at", - "timeColumnType": "timestamptz", - "where": [ - { - "name": "$__timeFilter", - "params": [], - "type": "macro" - }, - { - "datatype": "uuid", - "name": "", - "params": [ - "namespace_id", - "=", - "'$namespace'" - ], - "type": "expression" - }, - { - "datatype": "uuid", - "name": "", - "params": [ - "project_id", - "=", - "'$project'" - ], - "type": "expression" - } - ] - } - ], - "title": "SLA missed last runs ", - "type": "table" - }, - { - "datasource": "Optimus-Server-DB", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "axisSoftMin": 0, - "fillOpacity": 80, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1 - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 15, - "w": 23, - "x": 0, - "y": 35 - }, - "id": 7, - "options": { - "barRadius": 0, - "barWidth": 0.97, - "groupWidth": 0.7, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "orientation": "horizontal", - "showValue": "always", - "stacking": "none", - "text": {}, - "tooltip": { - "mode": "single", - "sort": "none" - }, - "xTickLabelRotation": 0, - "xTickLabelSpacing": 0 - }, - "targets": [ - { - "datasource": "Optimus-Server-DB", - "format": "table", - "group": [], - "metricColumn": "none", - "rawQuery": true, - "rawSql": "SELECT concat(job.name,'_',job_run.scheduled_at) as job_run_schedule, count(*) as reruns FROM job_run \ninner join job on job_run.job_id = job.id\nwhere\n $__timeFilter(scheduled_at) \n AND job_run.namespace_id = '$namespace' \n AND job_run.project_id = '$project'\ngroup by (job.name, job_run.job_id, job_run.scheduled_at ) \n", - "refId": "A", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "column" - } - ] - ], - "timeColumn": "time", - "where": [ - { - "name": "$__timeFilter", - "params": [], - "type": "macro" - } - ] - } - ], - "title": "job rerun counts ", - "type": "barchart" - }, - { - "datasource": "Optimus-Server-DB", - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "axisSoftMin": 0, - "fillOpacity": 85, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 0 - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 14, - "w": 24, - "x": 0, - "y": 50 - }, - "id": 2, - "options": { - "barRadius": 0, - "barWidth": 0.98, - "groupWidth": 0.7, - "legend": { - "calcs": [ - "last", - "max", - "min", - "mean" - ], - "displayMode": "table", - "placement": "bottom" - }, - "orientation": "auto", - "showValue": "always", - "stacking": "normal", - "text": {}, - "tooltip": { - "mode": "multi", - "sort": "none" - }, - "xTickLabelRotation": 15, - "xTickLabelSpacing": 0 - }, - "pluginVersion": "8.5.5", - "targets": [ - { - "datasource": "Optimus-Server-DB", - "format": "table", - "group": [], - "metricColumn": "none", - "rawQuery": true, - "rawSql": "SELECT\n timezone('utc',job_run.scheduled_at) as time,\n job_run.attempt as attempt, \n concat(timezone('utc',job_run.scheduled_at), '_',job_run.attempt ),\n sensor_run.duration as sensor_duration,\n task_run.duration as task_duration,\n hook_run.duration as hook_duration\nFROM job_run\nleft join sensor_run on sensor_run.job_run_id = job_run.job_run_id and sensor_run.job_run_attempt = job_run.attempt\nleft join task_run on task_run.job_run_id = job_run.job_run_id and task_run.job_run_attempt = job_run.attempt\nleft join hook_run on hook_run.job_run_id = job_run.job_run_id and hook_run.job_run_attempt = job_run.attempt\nWHERE\n $__timeFilter(scheduled_at) \n AND job_id = '$job_id' \n AND namespace_id = '$namespace' \n AND project_id = '$project'\n\nORDER BY scheduled_at , attempt", - "refId": "A", - "select": [ - [ - { - "params": [ - "duration" - ], - "type": "column" - } - ] - ], - "table": "job_run", - "timeColumn": "scheduled_at", - "timeColumnType": "timestamptz", - "where": [ - { - "name": "$__timeFilter", - "params": [], - "type": "macro" - }, - { - "datatype": "uuid", - "name": "", - "params": [ - "job_id", - "=", - "'$job_id'" - ], - "type": "expression" - } - ] - } - ], - "title": "job durations", - "transformations": [ - { - "id": "organize", - "options": { - "excludeByName": { - "attempt": true - }, - "indexByName": {}, - "renameByName": {} - } - } - ], - "type": "barchart" - } - ], - "refresh": "", - "schemaVersion": 30, - "style": "dark", - "tags": [], - "templating": { - "list": [ - { - "allValue": null, - "current": { - "selected": false, - "text": "dojo", - "value": "11595945-8f21-471f-a4c1-0b1cf1fa0352" - }, - "datasource": "Optimus-Server-DB", - "definition": "SELECT name AS __text, id AS __value FROM project", - "description": "optimus project name ", - "error": null, - "hide": 0, - "includeAll": false, - "label": null, - "multi": false, - "name": "project", - "options": [], - "query": "SELECT name AS __text, id AS __value FROM project", - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "type": "query" - }, - { - "allValue": null, - "current": { - "selected": false, - "text": "playground", - "value": "25914c11-4d80-4a49-8387-fede3140737d" - }, - "datasource": "Optimus-Server-DB", - "definition": "select id as __value, name as __text from namespace where project_id = '$project'", - "description": null, - "error": null, - "hide": 0, - "includeAll": false, - "label": null, - "multi": false, - "name": "namespace", - "options": [], - "query": "select id as __value, name as __text from namespace where project_id = '$project'", - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "type": "query" - }, - { - "allValue": null, - "current": { - "selected": false, - "text": "optimus.data_logs", - "value": "7a67d70c-3e33-4c9e-aab7-d5abcb923aa8" - }, - "datasource": "Optimus-Server-DB", - "definition": "select job.name as __text , job_run.job_id as __value \nfrom job_run \ninner join job on job_run.job_id = job.id\nWHERE $__timeFilter(scheduled_at)", - "description": "optimus job id ", - "error": null, - "hide": 0, - "includeAll": false, - "label": null, - "multi": false, - "name": "job_id", - "options": [], - "query": "select job.name as __text , job_run.job_id as __value \nfrom job_run \ninner join job on job_run.job_id = job.id\nWHERE $__timeFilter(scheduled_at)", - "refresh": 2, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "type": "query" - } - ] - }, - "time": { - "from": "now-30d", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "15m", - "30m", - "1h", - "2h", - "1d" - ] - }, - "timezone": "", - "title": "Optimus Dashboard", - "uid": "fD3G8mn4z", - "version": 9 -} \ No newline at end of file diff --git a/internal/telemetry/dashboards/scripts/add_monitoring_role.down.sql b/internal/telemetry/dashboards/scripts/add_monitoring_role.down.sql new file mode 100644 index 0000000000..301591f0bc --- /dev/null +++ b/internal/telemetry/dashboards/scripts/add_monitoring_role.down.sql @@ -0,0 +1,5 @@ +DROP OWNED BY MONITORING; + +DROP ROLE IF EXISTS MONITORING; + +DROP USER IF EXISTS MONITORING_USER; \ No newline at end of file diff --git a/internal/telemetry/dashboards/scripts/add_monitoring_role.up.sql b/internal/telemetry/dashboards/scripts/add_monitoring_role.up.sql new file mode 100644 index 0000000000..69a02cb407 --- /dev/null +++ b/internal/telemetry/dashboards/scripts/add_monitoring_role.up.sql @@ -0,0 +1,9 @@ +CREATE ROLE MONITORING; + +GRANT CONNECT ON DATABASE OPTIMUS TO MONITORING; + +GRANT SELECT ON JOB_RUN , SENSOR_RUN , TASK_RUN , HOOK_RUN , JOB_UPSTREAM, JOB , RESOURCE, PROJECT, NAMESPACE TO MONITORING; + +CREATE USER MONITORING_USER WITH PASSWORD '*********'; + +GRANT MONITORING TO MONITORING_USER; \ No newline at end of file diff --git a/internal/telemetry/prometheus.go b/internal/telemetry/prometheus.go index dcf7b66a32..949b8da7ef 100644 --- a/internal/telemetry/prometheus.go +++ b/internal/telemetry/prometheus.go @@ -2,14 +2,18 @@ package telemetry import ( "sort" + "sync" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" ) var ( - counterMetricMap = map[string]prometheus.Counter{} + counterMetricMap = map[string]prometheus.Counter{} + counterMetricMutex = sync.Mutex{} + gaugeMetricMap = map[string]prometheus.Gauge{} + gaugeMetricMutex = sync.Mutex{} ) func getKey(metric string, labels map[string]string) string { @@ -27,16 +31,28 @@ func getKey(metric string, labels map[string]string) string { func NewCounter(metric string, labels map[string]string) prometheus.Counter { metricKey := getKey(metric, labels) - if _, ok := counterMetricMap[metricKey]; !ok { - counterMetricMap[metricKey] = promauto.NewCounter(prometheus.CounterOpts{Name: metric, ConstLabels: labels}) + + counterMetricMutex.Lock() + defer counterMetricMutex.Unlock() + + if existingMetric, ok := counterMetricMap[metricKey]; ok { + return existingMetric } - return counterMetricMap[metricKey] + newMetric := promauto.NewCounter(prometheus.CounterOpts{Name: metric, ConstLabels: labels}) + counterMetricMap[metricKey] = newMetric + return newMetric } func NewGauge(metric string, labels map[string]string) prometheus.Gauge { metricKey := getKey(metric, labels) - if _, ok := gaugeMetricMap[metricKey]; !ok { - gaugeMetricMap[metricKey] = promauto.NewGauge(prometheus.GaugeOpts{Name: metric, ConstLabels: labels}) + + gaugeMetricMutex.Lock() + defer gaugeMetricMutex.Unlock() + + if existingMetric, ok := gaugeMetricMap[metricKey]; ok { + return existingMetric } - return gaugeMetricMap[metricKey] + newMetric := promauto.NewGauge(prometheus.GaugeOpts{Name: metric, ConstLabels: labels}) + gaugeMetricMap[metricKey] = newMetric + return newMetric } diff --git a/internal/telemetry/telemetry.go b/internal/telemetry/telemetry.go index 5669fc4eb0..ba7f4451f3 100644 --- a/internal/telemetry/telemetry.go +++ b/internal/telemetry/telemetry.go @@ -7,10 +7,10 @@ import ( "net/http/pprof" "time" - "github.com/odpf/salt/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/raystack/salt/log" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/jaeger" @@ -19,7 +19,7 @@ import ( tracesdk "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.4.0" - "github.com/odpf/optimus/config" + "github.com/raystack/optimus/config" ) const MetricWaitInterval = time.Second * 2 diff --git a/internal/utils/contains_test.go b/internal/utils/contains_test.go index b14fd58cc1..77a3eb7b3a 100644 --- a/internal/utils/contains_test.go +++ b/internal/utils/contains_test.go @@ -3,7 +3,7 @@ package utils_test import ( "testing" - "github.com/odpf/optimus/internal/utils" + "github.com/raystack/optimus/internal/utils" ) func TestContainsString(t *testing.T) { diff --git a/internal/utils/convert_test.go b/internal/utils/convert_test.go index fd31b6359f..326a64cf3f 100644 --- a/internal/utils/convert_test.go +++ b/internal/utils/convert_test.go @@ -6,7 +6,7 @@ import ( "github.com/AlecAivazis/survey/v2" "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/internal/utils" + "github.com/raystack/optimus/internal/utils" ) func TestConvert(t *testing.T) { diff --git a/internal/utils/file_test.go b/internal/utils/file_test.go index a35c527754..dd1312691a 100644 --- a/internal/utils/file_test.go +++ b/internal/utils/file_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/internal/utils" + "github.com/raystack/optimus/internal/utils" ) func TestIsPathOccupied(t *testing.T) { diff --git a/internal/utils/map_test.go b/internal/utils/map_test.go index 40e4fa00e9..636b37f905 100644 --- a/internal/utils/map_test.go +++ b/internal/utils/map_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/assert" "google.golang.org/protobuf/types/known/structpb" - "github.com/odpf/optimus/internal/utils" + "github.com/raystack/optimus/internal/utils" ) func TestMapHelper(t *testing.T) { diff --git a/internal/utils/proto_test.go b/internal/utils/proto_test.go index 4542630a1a..d0a473bba2 100644 --- a/internal/utils/proto_test.go +++ b/internal/utils/proto_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/internal/utils" + "github.com/raystack/optimus/internal/utils" ) func TestProtoHelper(t *testing.T) { diff --git a/internal/utils/validator_test.go b/internal/utils/validator_test.go index 1d9bb780d4..3d122bc7fb 100644 --- a/internal/utils/validator_test.go +++ b/internal/utils/validator_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/internal/utils" + "github.com/raystack/optimus/internal/utils" ) func TestValidator(t *testing.T) { diff --git a/internal/writer/check_job_specification_response_writer.go b/internal/writer/check_job_specification_response_writer.go index 0e2718ddc2..b6e7df36ac 100644 --- a/internal/writer/check_job_specification_response_writer.go +++ b/internal/writer/check_job_specification_response_writer.go @@ -1,7 +1,7 @@ package writer import ( - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) type checkJobSpecificationResponseWriter struct { diff --git a/internal/writer/deploy_resource_specification_response_writer.go b/internal/writer/deploy_resource_specification_response_writer.go index 56badd4a95..fb3b5de44c 100644 --- a/internal/writer/deploy_resource_specification_response_writer.go +++ b/internal/writer/deploy_resource_specification_response_writer.go @@ -1,7 +1,7 @@ package writer import ( - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) type deployResourceSpecificationResponseWriter struct { diff --git a/internal/writer/logwriter.go b/internal/writer/logwriter.go index b85ba4262d..2074e811eb 100644 --- a/internal/writer/logwriter.go +++ b/internal/writer/logwriter.go @@ -1,9 +1,9 @@ package writer import ( - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) type saltLogger struct { diff --git a/internal/writer/refresh_job_response_writer.go b/internal/writer/refresh_job_response_writer.go index a630176caf..fe5b2c9e24 100644 --- a/internal/writer/refresh_job_response_writer.go +++ b/internal/writer/refresh_job_response_writer.go @@ -1,7 +1,7 @@ package writer import ( - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) type RefreshJobResponseWriter interface { diff --git a/internal/writer/replace_all_job_specifications_response_writer.go b/internal/writer/replace_all_job_specifications_response_writer.go index 44b837c53d..da45f5b8c2 100644 --- a/internal/writer/replace_all_job_specifications_response_writer.go +++ b/internal/writer/replace_all_job_specifications_response_writer.go @@ -1,7 +1,7 @@ package writer import ( - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) type ReplaceAllJobSpecificationsResponseWriter interface { diff --git a/internal/writer/writer.go b/internal/writer/writer.go index 005599c0fb..d878681d25 100644 --- a/internal/writer/writer.go +++ b/internal/writer/writer.go @@ -1,7 +1,7 @@ package writer import ( - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) type LogLevel int diff --git a/main.go b/main.go index 61237030d9..1812ba1af3 100644 --- a/main.go +++ b/main.go @@ -9,10 +9,10 @@ import ( _ "go.uber.org/automaxprocs" - clientCmd "github.com/odpf/optimus/client/cmd" - _ "github.com/odpf/optimus/client/extension/provider" - server "github.com/odpf/optimus/server/cmd" - "github.com/odpf/optimus/server/cmd/migration" + clientCmd "github.com/raystack/optimus/client/cmd" + _ "github.com/raystack/optimus/client/extension/provider" + server "github.com/raystack/optimus/server/cmd" + "github.com/raystack/optimus/server/cmd/migration" ) var errRequestFail = errors.New("🔥 unable to complete request successfully") diff --git a/plugin/binary/plugin.go b/plugin/binary/plugin.go index 9c39af56ad..a7b47fb573 100644 --- a/plugin/binary/plugin.go +++ b/plugin/binary/plugin.go @@ -9,9 +9,9 @@ import ( "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-plugin" - "github.com/odpf/optimus/internal/models" - "github.com/odpf/optimus/plugin/v1beta1/dependencyresolver" - oplugin "github.com/odpf/optimus/sdk/plugin" + "github.com/raystack/optimus/internal/models" + "github.com/raystack/optimus/plugin/v1beta1/dependencyresolver" + oplugin "github.com/raystack/optimus/sdk/plugin" ) var ( diff --git a/plugin/plugin.go b/plugin/plugin.go index 025fc1f528..c93696b5d6 100644 --- a/plugin/plugin.go +++ b/plugin/plugin.go @@ -9,11 +9,11 @@ import ( "github.com/hashicorp/go-hclog" - "github.com/odpf/optimus/internal/models" - "github.com/odpf/optimus/plugin/binary" - "github.com/odpf/optimus/plugin/v1beta1/dependencyresolver" - "github.com/odpf/optimus/plugin/yaml" - "github.com/odpf/optimus/sdk/plugin" + "github.com/raystack/optimus/internal/models" + "github.com/raystack/optimus/plugin/binary" + "github.com/raystack/optimus/plugin/v1beta1/dependencyresolver" + "github.com/raystack/optimus/plugin/yaml" + "github.com/raystack/optimus/sdk/plugin" ) func Initialize(pluginLogger hclog.Logger, arg ...string) (*models.PluginRepository, error) { diff --git a/plugin/plugin_manager.go b/plugin/plugin_manager.go index 722915063b..b9aeaeb9bf 100644 --- a/plugin/plugin_manager.go +++ b/plugin/plugin_manager.go @@ -12,8 +12,8 @@ import ( getter "github.com/hashicorp/go-getter" "github.com/hashicorp/go-hclog" - "github.com/odpf/optimus/config" - "github.com/odpf/optimus/plugin/yaml" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/plugin/yaml" ) var ( diff --git a/plugin/v1beta1/dependencyresolver/adapter.go b/plugin/v1beta1/dependencyresolver/adapter.go index 4bcae1f694..bda4163135 100644 --- a/plugin/v1beta1/dependencyresolver/adapter.go +++ b/plugin/v1beta1/dependencyresolver/adapter.go @@ -1,8 +1,8 @@ package dependencyresolver import ( - pb "github.com/odpf/optimus/protos/odpf/optimus/plugins/v1beta1" - "github.com/odpf/optimus/sdk/plugin" + pb "github.com/raystack/optimus/protos/raystack/optimus/plugins/v1beta1" + "github.com/raystack/optimus/sdk/plugin" ) func adaptConfigsToProto(c plugin.Configs) *pb.Configs { diff --git a/plugin/v1beta1/dependencyresolver/client.go b/plugin/v1beta1/dependencyresolver/client.go index 1d705e6f87..db28b1244a 100644 --- a/plugin/v1beta1/dependencyresolver/client.go +++ b/plugin/v1beta1/dependencyresolver/client.go @@ -12,10 +12,8 @@ import ( "google.golang.org/grpc/metadata" "google.golang.org/protobuf/types/known/timestamppb" - "github.com/odpf/optimus/internal/utils" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" - pbp "github.com/odpf/optimus/protos/odpf/optimus/plugins/v1beta1" - "github.com/odpf/optimus/sdk/plugin" + pbp "github.com/raystack/optimus/protos/raystack/optimus/plugins/v1beta1" + "github.com/raystack/optimus/sdk/plugin" ) const ( @@ -90,12 +88,12 @@ func (m *GRPCClient) CompileAssets(ctx context.Context, request plugin.CompileAs _, span := tracer.Start(ctx, "CompileAssets") defer span.End() - var instanceData []*pb.InstanceSpecData + var instanceData []*pbp.InstanceData for _, inst := range request.InstanceData { - instanceData = append(instanceData, &pb.InstanceSpecData{ + instanceData = append(instanceData, &pbp.InstanceData{ Name: inst.Name, Value: inst.Value, - Type: pb.InstanceSpecData_Type(pb.InstanceSpecData_Type_value[utils.ToEnumProto(inst.Type, "type")]), + Type: inst.Type, }) } diff --git a/plugin/v1beta1/dependencyresolver/connector.go b/plugin/v1beta1/dependencyresolver/connector.go index cddd020e5d..7aa1aa15d0 100644 --- a/plugin/v1beta1/dependencyresolver/connector.go +++ b/plugin/v1beta1/dependencyresolver/connector.go @@ -8,8 +8,8 @@ import ( "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" "google.golang.org/grpc" - pbp "github.com/odpf/optimus/protos/odpf/optimus/plugins/v1beta1" - oplugin "github.com/odpf/optimus/sdk/plugin" + pbp "github.com/raystack/optimus/protos/raystack/optimus/plugins/v1beta1" + oplugin "github.com/raystack/optimus/sdk/plugin" ) var _ plugin.GRPCPlugin = &Connector{} diff --git a/plugin/v1beta1/dependencyresolver/server.go b/plugin/v1beta1/dependencyresolver/server.go index 867b4ec768..9f839358cb 100644 --- a/plugin/v1beta1/dependencyresolver/server.go +++ b/plugin/v1beta1/dependencyresolver/server.go @@ -3,9 +3,8 @@ package dependencyresolver import ( "context" - "github.com/odpf/optimus/internal/utils" - pbp "github.com/odpf/optimus/protos/odpf/optimus/plugins/v1beta1" - "github.com/odpf/optimus/sdk/plugin" + pbp "github.com/raystack/optimus/protos/raystack/optimus/plugins/v1beta1" + "github.com/raystack/optimus/sdk/plugin" ) // GRPCServer will be used by plugins this is working as proto adapter @@ -54,7 +53,7 @@ func (s *GRPCServer) CompileAssets(ctx context.Context, req *pbp.CompileAssetsRe instanceData = append(instanceData, plugin.JobRunSpecData{ Name: inst.Name, Value: inst.Value, - Type: utils.FromEnumProto(inst.Type.String(), "type"), + Type: inst.Type, }) } diff --git a/plugin/yaml/plugin.go b/plugin/yaml/plugin.go index 683502f2c9..77d6887575 100644 --- a/plugin/yaml/plugin.go +++ b/plugin/yaml/plugin.go @@ -11,8 +11,8 @@ import ( "github.com/spf13/afero" "gopkg.in/yaml.v2" - "github.com/odpf/optimus/internal/models" - "github.com/odpf/optimus/sdk/plugin" + "github.com/raystack/optimus/internal/models" + "github.com/raystack/optimus/sdk/plugin" ) const ( diff --git a/plugin/yaml/plugin_test.go b/plugin/yaml/plugin_test.go index 3187c34cdb..c103c32169 100644 --- a/plugin/yaml/plugin_test.go +++ b/plugin/yaml/plugin_test.go @@ -8,9 +8,9 @@ import ( "github.com/hashicorp/go-hclog" "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/internal/models" - "github.com/odpf/optimus/plugin/yaml" - "github.com/odpf/optimus/sdk/plugin" + "github.com/raystack/optimus/internal/models" + "github.com/raystack/optimus/plugin/yaml" + "github.com/raystack/optimus/sdk/plugin" ) type mockYamlMod struct { @@ -53,7 +53,7 @@ func TestYamlPlugin(t *testing.T) { expectedInfo := &plugin.Info{ Name: "bq2bqtest", Description: "Testing", - Image: "docker.io/odpf/optimus-task-bq2bq-executor:latest", + Image: "docker.io/raystack/optimus-task-bq2bq-executor:latest", Entrypoint: plugin.Entrypoint{ Shell: "/bin/bash", Script: "sleep 100; sleep 150", diff --git a/plugin/yaml/tests/sample_plugin.yaml b/plugin/yaml/tests/sample_plugin.yaml index 75e4eb3627..8b7c012ac5 100644 --- a/plugin/yaml/tests/sample_plugin.yaml +++ b/plugin/yaml/tests/sample_plugin.yaml @@ -5,7 +5,7 @@ pluginmods: - cli - dependencyresolver pluginversion: latest -image: docker.io/odpf/optimus-task-bq2bq-executor:latest +image: docker.io/raystack/optimus-task-bq2bq-executor:latest entrypoint: shell: "/bin/bash" script: |- @@ -19,9 +19,9 @@ questions: minlength: 3 defaultconfig: -- name: TEST - value: "{{.test}}" + - name: TEST + value: "{{.test}}" defaultassets: - name: query.sql - value: Select * from "project.dataset.table"; \ No newline at end of file + value: Select * from "project.dataset.table"; diff --git a/plugin/yaml/tests/sample_plugin_without_shell.yaml b/plugin/yaml/tests/sample_plugin_without_shell.yaml index f45e6bfbf9..098aff64dc 100644 --- a/plugin/yaml/tests/sample_plugin_without_shell.yaml +++ b/plugin/yaml/tests/sample_plugin_without_shell.yaml @@ -5,7 +5,7 @@ pluginmods: - cli - dependencyresolver pluginversion: latest -image: docker.io/odpf/optimus-task-bq2bq-executor:latest +image: docker.io/raystack/optimus-task-bq2bq-executor:latest entrypoint: script: "sleep 100" @@ -16,9 +16,9 @@ questions: minlength: 3 defaultconfig: -- name: TEST - value: "{{.test}}" + - name: TEST + value: "{{.test}}" defaultassets: - name: query.sql - value: Select * from "project.dataset.table"; \ No newline at end of file + value: Select * from "project.dataset.table"; diff --git a/protos/odpf/optimus/cluster/v1beta1/command.pb.go b/protos/odpf/optimus/cluster/v1beta1/command.pb.go deleted file mode 100644 index eb7cfdbbcd..0000000000 --- a/protos/odpf/optimus/cluster/v1beta1/command.pb.go +++ /dev/null @@ -1,511 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.0 -// protoc (unknown) -// source: odpf/optimus/cluster/v1beta1/command.proto - -package optimus - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type CommandLogType int32 - -const ( - CommandLogType_COMMAND_LOG_TYPE_UNSPECIFIED CommandLogType = 0 - CommandLogType_COMMAND_LOG_TYPE_NOOP CommandLogType = 1 - CommandLogType_COMMAND_LOG_TYPE_SCHEDULE_JOB CommandLogType = 2 - CommandLogType_COMMAND_LOG_TYPE_UPDATE_JOB CommandLogType = 3 -) - -// Enum value maps for CommandLogType. -var ( - CommandLogType_name = map[int32]string{ - 0: "COMMAND_LOG_TYPE_UNSPECIFIED", - 1: "COMMAND_LOG_TYPE_NOOP", - 2: "COMMAND_LOG_TYPE_SCHEDULE_JOB", - 3: "COMMAND_LOG_TYPE_UPDATE_JOB", - } - CommandLogType_value = map[string]int32{ - "COMMAND_LOG_TYPE_UNSPECIFIED": 0, - "COMMAND_LOG_TYPE_NOOP": 1, - "COMMAND_LOG_TYPE_SCHEDULE_JOB": 2, - "COMMAND_LOG_TYPE_UPDATE_JOB": 3, - } -) - -func (x CommandLogType) Enum() *CommandLogType { - p := new(CommandLogType) - *p = x - return p -} - -func (x CommandLogType) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (CommandLogType) Descriptor() protoreflect.EnumDescriptor { - return file_odpf_optimus_cluster_v1beta1_command_proto_enumTypes[0].Descriptor() -} - -func (CommandLogType) Type() protoreflect.EnumType { - return &file_odpf_optimus_cluster_v1beta1_command_proto_enumTypes[0] -} - -func (x CommandLogType) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use CommandLogType.Descriptor instead. -func (CommandLogType) EnumDescriptor() ([]byte, []int) { - return file_odpf_optimus_cluster_v1beta1_command_proto_rawDescGZIP(), []int{0} -} - -type CommandLog struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Type CommandLogType `protobuf:"varint,1,opt,name=type,proto3,enum=odpf.optimus.cluster.v1beta1.CommandLogType" json:"type,omitempty"` - Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` -} - -func (x *CommandLog) Reset() { - *x = CommandLog{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_cluster_v1beta1_command_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CommandLog) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CommandLog) ProtoMessage() {} - -func (x *CommandLog) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_cluster_v1beta1_command_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CommandLog.ProtoReflect.Descriptor instead. -func (*CommandLog) Descriptor() ([]byte, []int) { - return file_odpf_optimus_cluster_v1beta1_command_proto_rawDescGZIP(), []int{0} -} - -func (x *CommandLog) GetType() CommandLogType { - if x != nil { - return x.Type - } - return CommandLogType_COMMAND_LOG_TYPE_UNSPECIFIED -} - -func (x *CommandLog) GetPayload() []byte { - if x != nil { - return x.Payload - } - return nil -} - -type CommandNoop struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` -} - -func (x *CommandNoop) Reset() { - *x = CommandNoop{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_cluster_v1beta1_command_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CommandNoop) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CommandNoop) ProtoMessage() {} - -func (x *CommandNoop) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_cluster_v1beta1_command_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CommandNoop.ProtoReflect.Descriptor instead. -func (*CommandNoop) Descriptor() ([]byte, []int) { - return file_odpf_optimus_cluster_v1beta1_command_proto_rawDescGZIP(), []int{1} -} - -func (x *CommandNoop) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -// CommandScheduleJob will be sent to assign job for execution to a peer -type CommandScheduleJob struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // peer_id is the node name to which this job is assigned to get - // executed - PeerId string `protobuf:"bytes,1,opt,name=peer_id,json=peerId,proto3" json:"peer_id,omitempty"` - RunIds []string `protobuf:"bytes,2,rep,name=run_ids,json=runIds,proto3" json:"run_ids,omitempty"` -} - -func (x *CommandScheduleJob) Reset() { - *x = CommandScheduleJob{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_cluster_v1beta1_command_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CommandScheduleJob) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CommandScheduleJob) ProtoMessage() {} - -func (x *CommandScheduleJob) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_cluster_v1beta1_command_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CommandScheduleJob.ProtoReflect.Descriptor instead. -func (*CommandScheduleJob) Descriptor() ([]byte, []int) { - return file_odpf_optimus_cluster_v1beta1_command_proto_rawDescGZIP(), []int{2} -} - -func (x *CommandScheduleJob) GetPeerId() string { - if x != nil { - return x.PeerId - } - return "" -} - -func (x *CommandScheduleJob) GetRunIds() []string { - if x != nil { - return x.RunIds - } - return nil -} - -// CommandUpdateJob will be sent to update the attributes of job which was -// previously scheduled for execution -type CommandUpdateJob struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - PeerId string `protobuf:"bytes,1,opt,name=peer_id,json=peerId,proto3" json:"peer_id,omitempty"` - Patches []*CommandUpdateJob_Patch `protobuf:"bytes,2,rep,name=patches,proto3" json:"patches,omitempty"` -} - -func (x *CommandUpdateJob) Reset() { - *x = CommandUpdateJob{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_cluster_v1beta1_command_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CommandUpdateJob) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CommandUpdateJob) ProtoMessage() {} - -func (x *CommandUpdateJob) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_cluster_v1beta1_command_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CommandUpdateJob.ProtoReflect.Descriptor instead. -func (*CommandUpdateJob) Descriptor() ([]byte, []int) { - return file_odpf_optimus_cluster_v1beta1_command_proto_rawDescGZIP(), []int{3} -} - -func (x *CommandUpdateJob) GetPeerId() string { - if x != nil { - return x.PeerId - } - return "" -} - -func (x *CommandUpdateJob) GetPatches() []*CommandUpdateJob_Patch { - if x != nil { - return x.Patches - } - return nil -} - -type CommandUpdateJob_Patch struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - RunId string `protobuf:"bytes,1,opt,name=run_id,json=runId,proto3" json:"run_id,omitempty"` - Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` -} - -func (x *CommandUpdateJob_Patch) Reset() { - *x = CommandUpdateJob_Patch{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_cluster_v1beta1_command_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CommandUpdateJob_Patch) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CommandUpdateJob_Patch) ProtoMessage() {} - -func (x *CommandUpdateJob_Patch) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_cluster_v1beta1_command_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CommandUpdateJob_Patch.ProtoReflect.Descriptor instead. -func (*CommandUpdateJob_Patch) Descriptor() ([]byte, []int) { - return file_odpf_optimus_cluster_v1beta1_command_proto_rawDescGZIP(), []int{3, 0} -} - -func (x *CommandUpdateJob_Patch) GetRunId() string { - if x != nil { - return x.RunId - } - return "" -} - -func (x *CommandUpdateJob_Patch) GetStatus() string { - if x != nil { - return x.Status - } - return "" -} - -var File_odpf_optimus_cluster_v1beta1_command_proto protoreflect.FileDescriptor - -var file_odpf_optimus_cluster_v1beta1_command_proto_rawDesc = []byte{ - 0x0a, 0x2a, 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x63, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x6f, 0x64, - 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x22, 0x68, 0x0a, 0x0a, 0x43, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4c, 0x6f, 0x67, 0x12, 0x40, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, - 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4c, 0x6f, 0x67, - 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, - 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, - 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x1d, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4e, - 0x6f, 0x6f, 0x70, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x02, 0x69, 0x64, 0x22, 0x46, 0x0a, 0x12, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x53, 0x63, - 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x65, 0x65, - 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, - 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x06, 0x72, 0x75, 0x6e, 0x49, 0x64, 0x73, 0x22, 0xb3, 0x01, 0x0a, 0x10, - 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, - 0x12, 0x17, 0x0a, 0x07, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x64, 0x12, 0x4e, 0x0a, 0x07, 0x70, 0x61, 0x74, - 0x63, 0x68, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x6f, 0x64, 0x70, - 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, - 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, - 0x52, 0x07, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x1a, 0x36, 0x0a, 0x05, 0x50, 0x61, 0x74, - 0x63, 0x68, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x72, 0x75, 0x6e, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x2a, 0x91, 0x01, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4c, 0x6f, 0x67, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x1c, 0x43, 0x4f, 0x4d, 0x4d, 0x41, 0x4e, 0x44, 0x5f, - 0x4c, 0x4f, 0x47, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, - 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x43, 0x4f, 0x4d, 0x4d, 0x41, 0x4e, - 0x44, 0x5f, 0x4c, 0x4f, 0x47, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x4f, 0x4f, 0x50, 0x10, - 0x01, 0x12, 0x21, 0x0a, 0x1d, 0x43, 0x4f, 0x4d, 0x4d, 0x41, 0x4e, 0x44, 0x5f, 0x4c, 0x4f, 0x47, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x5f, 0x4a, - 0x4f, 0x42, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x43, 0x4f, 0x4d, 0x4d, 0x41, 0x4e, 0x44, 0x5f, - 0x4c, 0x4f, 0x47, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, - 0x4a, 0x4f, 0x42, 0x10, 0x03, 0x42, 0x20, 0x5a, 0x1e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, - 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_odpf_optimus_cluster_v1beta1_command_proto_rawDescOnce sync.Once - file_odpf_optimus_cluster_v1beta1_command_proto_rawDescData = file_odpf_optimus_cluster_v1beta1_command_proto_rawDesc -) - -func file_odpf_optimus_cluster_v1beta1_command_proto_rawDescGZIP() []byte { - file_odpf_optimus_cluster_v1beta1_command_proto_rawDescOnce.Do(func() { - file_odpf_optimus_cluster_v1beta1_command_proto_rawDescData = protoimpl.X.CompressGZIP(file_odpf_optimus_cluster_v1beta1_command_proto_rawDescData) - }) - return file_odpf_optimus_cluster_v1beta1_command_proto_rawDescData -} - -var file_odpf_optimus_cluster_v1beta1_command_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_odpf_optimus_cluster_v1beta1_command_proto_msgTypes = make([]protoimpl.MessageInfo, 5) -var file_odpf_optimus_cluster_v1beta1_command_proto_goTypes = []interface{}{ - (CommandLogType)(0), // 0: odpf.optimus.cluster.v1beta1.CommandLogType - (*CommandLog)(nil), // 1: odpf.optimus.cluster.v1beta1.CommandLog - (*CommandNoop)(nil), // 2: odpf.optimus.cluster.v1beta1.CommandNoop - (*CommandScheduleJob)(nil), // 3: odpf.optimus.cluster.v1beta1.CommandScheduleJob - (*CommandUpdateJob)(nil), // 4: odpf.optimus.cluster.v1beta1.CommandUpdateJob - (*CommandUpdateJob_Patch)(nil), // 5: odpf.optimus.cluster.v1beta1.CommandUpdateJob.Patch -} -var file_odpf_optimus_cluster_v1beta1_command_proto_depIdxs = []int32{ - 0, // 0: odpf.optimus.cluster.v1beta1.CommandLog.type:type_name -> odpf.optimus.cluster.v1beta1.CommandLogType - 5, // 1: odpf.optimus.cluster.v1beta1.CommandUpdateJob.patches:type_name -> odpf.optimus.cluster.v1beta1.CommandUpdateJob.Patch - 2, // [2:2] is the sub-list for method output_type - 2, // [2:2] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name -} - -func init() { file_odpf_optimus_cluster_v1beta1_command_proto_init() } -func file_odpf_optimus_cluster_v1beta1_command_proto_init() { - if File_odpf_optimus_cluster_v1beta1_command_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_odpf_optimus_cluster_v1beta1_command_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CommandLog); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_cluster_v1beta1_command_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CommandNoop); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_cluster_v1beta1_command_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CommandScheduleJob); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_cluster_v1beta1_command_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CommandUpdateJob); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_cluster_v1beta1_command_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CommandUpdateJob_Patch); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_odpf_optimus_cluster_v1beta1_command_proto_rawDesc, - NumEnums: 1, - NumMessages: 5, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_odpf_optimus_cluster_v1beta1_command_proto_goTypes, - DependencyIndexes: file_odpf_optimus_cluster_v1beta1_command_proto_depIdxs, - EnumInfos: file_odpf_optimus_cluster_v1beta1_command_proto_enumTypes, - MessageInfos: file_odpf_optimus_cluster_v1beta1_command_proto_msgTypes, - }.Build() - File_odpf_optimus_cluster_v1beta1_command_proto = out.File - file_odpf_optimus_cluster_v1beta1_command_proto_rawDesc = nil - file_odpf_optimus_cluster_v1beta1_command_proto_goTypes = nil - file_odpf_optimus_cluster_v1beta1_command_proto_depIdxs = nil -} diff --git a/protos/odpf/optimus/core/v1beta1/backup.pb.go b/protos/odpf/optimus/core/v1beta1/backup.pb.go deleted file mode 100644 index 3e80171cc2..0000000000 --- a/protos/odpf/optimus/core/v1beta1/backup.pb.go +++ /dev/null @@ -1,859 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.0 -// protoc (unknown) -// source: odpf/optimus/core/v1beta1/backup.proto - -package optimus - -import ( - _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" - _ "google.golang.org/genproto/googleapis/api/annotations" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - timestamppb "google.golang.org/protobuf/types/known/timestamppb" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type IgnoredResource struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Reason string `protobuf:"bytes,2,opt,name=reason,proto3" json:"reason,omitempty"` -} - -func (x *IgnoredResource) Reset() { - *x = IgnoredResource{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_backup_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *IgnoredResource) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*IgnoredResource) ProtoMessage() {} - -func (x *IgnoredResource) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_backup_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use IgnoredResource.ProtoReflect.Descriptor instead. -func (*IgnoredResource) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_backup_proto_rawDescGZIP(), []int{0} -} - -func (x *IgnoredResource) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *IgnoredResource) GetReason() string { - if x != nil { - return x.Reason - } - return "" -} - -type CreateBackupRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` - DatastoreName string `protobuf:"bytes,2,opt,name=datastore_name,json=datastoreName,proto3" json:"datastore_name,omitempty"` - NamespaceName string `protobuf:"bytes,4,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` - Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty"` - Config map[string]string `protobuf:"bytes,7,rep,name=config,proto3" json:"config,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - ResourceNames []string `protobuf:"bytes,9,rep,name=resource_names,json=resourceNames,proto3" json:"resource_names,omitempty"` -} - -func (x *CreateBackupRequest) Reset() { - *x = CreateBackupRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_backup_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CreateBackupRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateBackupRequest) ProtoMessage() {} - -func (x *CreateBackupRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_backup_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CreateBackupRequest.ProtoReflect.Descriptor instead. -func (*CreateBackupRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_backup_proto_rawDescGZIP(), []int{1} -} - -func (x *CreateBackupRequest) GetProjectName() string { - if x != nil { - return x.ProjectName - } - return "" -} - -func (x *CreateBackupRequest) GetDatastoreName() string { - if x != nil { - return x.DatastoreName - } - return "" -} - -func (x *CreateBackupRequest) GetNamespaceName() string { - if x != nil { - return x.NamespaceName - } - return "" -} - -func (x *CreateBackupRequest) GetDescription() string { - if x != nil { - return x.Description - } - return "" -} - -func (x *CreateBackupRequest) GetConfig() map[string]string { - if x != nil { - return x.Config - } - return nil -} - -func (x *CreateBackupRequest) GetResourceNames() []string { - if x != nil { - return x.ResourceNames - } - return nil -} - -type CreateBackupResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ResourceNames []string `protobuf:"bytes,1,rep,name=resource_names,json=resourceNames,proto3" json:"resource_names,omitempty"` - IgnoredResources []*IgnoredResource `protobuf:"bytes,3,rep,name=ignored_resources,json=ignoredResources,proto3" json:"ignored_resources,omitempty"` - BackupId string `protobuf:"bytes,4,opt,name=backup_id,json=backupId,proto3" json:"backup_id,omitempty"` -} - -func (x *CreateBackupResponse) Reset() { - *x = CreateBackupResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_backup_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CreateBackupResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateBackupResponse) ProtoMessage() {} - -func (x *CreateBackupResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_backup_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CreateBackupResponse.ProtoReflect.Descriptor instead. -func (*CreateBackupResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_backup_proto_rawDescGZIP(), []int{2} -} - -func (x *CreateBackupResponse) GetResourceNames() []string { - if x != nil { - return x.ResourceNames - } - return nil -} - -func (x *CreateBackupResponse) GetIgnoredResources() []*IgnoredResource { - if x != nil { - return x.IgnoredResources - } - return nil -} - -func (x *CreateBackupResponse) GetBackupId() string { - if x != nil { - return x.BackupId - } - return "" -} - -type ListBackupsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` - DatastoreName string `protobuf:"bytes,2,opt,name=datastore_name,json=datastoreName,proto3" json:"datastore_name,omitempty"` - NamespaceName string `protobuf:"bytes,3,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` -} - -func (x *ListBackupsRequest) Reset() { - *x = ListBackupsRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_backup_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ListBackupsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListBackupsRequest) ProtoMessage() {} - -func (x *ListBackupsRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_backup_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListBackupsRequest.ProtoReflect.Descriptor instead. -func (*ListBackupsRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_backup_proto_rawDescGZIP(), []int{3} -} - -func (x *ListBackupsRequest) GetProjectName() string { - if x != nil { - return x.ProjectName - } - return "" -} - -func (x *ListBackupsRequest) GetDatastoreName() string { - if x != nil { - return x.DatastoreName - } - return "" -} - -func (x *ListBackupsRequest) GetNamespaceName() string { - if x != nil { - return x.NamespaceName - } - return "" -} - -type ListBackupsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Backups []*BackupSpec `protobuf:"bytes,1,rep,name=backups,proto3" json:"backups,omitempty"` -} - -func (x *ListBackupsResponse) Reset() { - *x = ListBackupsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_backup_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ListBackupsResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListBackupsResponse) ProtoMessage() {} - -func (x *ListBackupsResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_backup_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListBackupsResponse.ProtoReflect.Descriptor instead. -func (*ListBackupsResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_backup_proto_rawDescGZIP(), []int{4} -} - -func (x *ListBackupsResponse) GetBackups() []*BackupSpec { - if x != nil { - return x.Backups - } - return nil -} - -type BackupSpec struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - CreatedAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` - Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` - Config map[string]string `protobuf:"bytes,5,rep,name=config,proto3" json:"config,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - ResourceNames []string `protobuf:"bytes,6,rep,name=resource_names,json=resourceNames,proto3" json:"resource_names,omitempty"` -} - -func (x *BackupSpec) Reset() { - *x = BackupSpec{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_backup_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *BackupSpec) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BackupSpec) ProtoMessage() {} - -func (x *BackupSpec) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_backup_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BackupSpec.ProtoReflect.Descriptor instead. -func (*BackupSpec) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_backup_proto_rawDescGZIP(), []int{5} -} - -func (x *BackupSpec) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *BackupSpec) GetCreatedAt() *timestamppb.Timestamp { - if x != nil { - return x.CreatedAt - } - return nil -} - -func (x *BackupSpec) GetDescription() string { - if x != nil { - return x.Description - } - return "" -} - -func (x *BackupSpec) GetConfig() map[string]string { - if x != nil { - return x.Config - } - return nil -} - -func (x *BackupSpec) GetResourceNames() []string { - if x != nil { - return x.ResourceNames - } - return nil -} - -type GetBackupRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` - DatastoreName string `protobuf:"bytes,2,opt,name=datastore_name,json=datastoreName,proto3" json:"datastore_name,omitempty"` - NamespaceName string `protobuf:"bytes,3,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` - Id string `protobuf:"bytes,4,opt,name=id,proto3" json:"id,omitempty"` -} - -func (x *GetBackupRequest) Reset() { - *x = GetBackupRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_backup_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetBackupRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetBackupRequest) ProtoMessage() {} - -func (x *GetBackupRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_backup_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetBackupRequest.ProtoReflect.Descriptor instead. -func (*GetBackupRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_backup_proto_rawDescGZIP(), []int{6} -} - -func (x *GetBackupRequest) GetProjectName() string { - if x != nil { - return x.ProjectName - } - return "" -} - -func (x *GetBackupRequest) GetDatastoreName() string { - if x != nil { - return x.DatastoreName - } - return "" -} - -func (x *GetBackupRequest) GetNamespaceName() string { - if x != nil { - return x.NamespaceName - } - return "" -} - -func (x *GetBackupRequest) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -type GetBackupResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Spec *BackupSpec `protobuf:"bytes,1,opt,name=spec,proto3" json:"spec,omitempty"` -} - -func (x *GetBackupResponse) Reset() { - *x = GetBackupResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_backup_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetBackupResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetBackupResponse) ProtoMessage() {} - -func (x *GetBackupResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_backup_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetBackupResponse.ProtoReflect.Descriptor instead. -func (*GetBackupResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_backup_proto_rawDescGZIP(), []int{7} -} - -func (x *GetBackupResponse) GetSpec() *BackupSpec { - if x != nil { - return x.Spec - } - return nil -} - -var File_odpf_optimus_core_v1beta1_backup_proto protoreflect.FileDescriptor - -var file_odpf_optimus_core_v1beta1_backup_proto_rawDesc = []byte{ - 0x0a, 0x26, 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, - 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x62, 0x61, 0x63, 0x6b, - 0x75, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, - 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, - 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, - 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0x3d, 0x0a, 0x0f, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, - 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, - 0x6e, 0x22, 0xf0, 0x02, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x61, 0x63, 0x6b, - 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, - 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x52, 0x0a, 0x06, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x6f, - 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, - 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x4a, 0x04, - 0x08, 0x08, 0x10, 0x09, 0x22, 0xb9, 0x01, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, - 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, - 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x73, 0x12, 0x57, 0x0a, 0x11, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x5f, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x2a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x49, 0x67, 0x6e, 0x6f, - 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x10, 0x69, 0x67, 0x6e, - 0x6f, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x1b, 0x0a, - 0x09, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x49, 0x64, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, - 0x22, 0x85, 0x01, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x61, - 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x56, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, - 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x3f, 0x0a, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x25, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x42, 0x61, 0x63, - 0x6b, 0x75, 0x70, 0x53, 0x70, 0x65, 0x63, 0x52, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, - 0x22, 0xac, 0x02, 0x0a, 0x0a, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x70, 0x65, 0x63, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, - 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x49, 0x0a, 0x06, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x6f, - 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, - 0x70, 0x65, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x1a, 0x39, - 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, - 0x93, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0d, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, - 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x54, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, - 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x04, 0x73, 0x70, - 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, - 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x70, 0x65, 0x63, 0x52, - 0x04, 0x73, 0x70, 0x65, 0x63, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x32, 0x8e, 0x05, 0x0a, 0x0d, - 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xd8, 0x01, - 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x2e, - 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, - 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x67, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x61, 0x22, 0x5c, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x7d, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x7b, 0x64, - 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x62, - 0x61, 0x63, 0x6b, 0x75, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0xd2, 0x01, 0x0a, 0x0b, 0x4c, 0x69, 0x73, - 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x2d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, - 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, - 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x64, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x5e, 0x12, - 0x5c, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, - 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x64, 0x61, 0x74, 0x61, - 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x7b, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0xcc, 0x01, - 0x0a, 0x09, 0x47, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x2b, 0x2e, 0x6f, 0x64, - 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, - 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, - 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x64, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x5e, 0x12, 0x5c, - 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, - 0x7b, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, - 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x42, 0x8d, 0x01, 0x0a, - 0x16, 0x69, 0x6f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2e, - 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x42, 0x14, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x50, 0x01, 0x5a, - 0x1e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x64, 0x70, 0x66, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x92, - 0x41, 0x3a, 0x12, 0x05, 0x32, 0x03, 0x30, 0x2e, 0x31, 0x1a, 0x0e, 0x31, 0x32, 0x37, 0x2e, 0x30, - 0x2e, 0x30, 0x2e, 0x31, 0x3a, 0x39, 0x31, 0x30, 0x30, 0x22, 0x04, 0x2f, 0x61, 0x70, 0x69, 0x2a, - 0x01, 0x01, 0x72, 0x18, 0x0a, 0x16, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x20, 0x42, 0x61, - 0x63, 0x6b, 0x75, 0x70, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_odpf_optimus_core_v1beta1_backup_proto_rawDescOnce sync.Once - file_odpf_optimus_core_v1beta1_backup_proto_rawDescData = file_odpf_optimus_core_v1beta1_backup_proto_rawDesc -) - -func file_odpf_optimus_core_v1beta1_backup_proto_rawDescGZIP() []byte { - file_odpf_optimus_core_v1beta1_backup_proto_rawDescOnce.Do(func() { - file_odpf_optimus_core_v1beta1_backup_proto_rawDescData = protoimpl.X.CompressGZIP(file_odpf_optimus_core_v1beta1_backup_proto_rawDescData) - }) - return file_odpf_optimus_core_v1beta1_backup_proto_rawDescData -} - -var file_odpf_optimus_core_v1beta1_backup_proto_msgTypes = make([]protoimpl.MessageInfo, 10) -var file_odpf_optimus_core_v1beta1_backup_proto_goTypes = []interface{}{ - (*IgnoredResource)(nil), // 0: odpf.optimus.core.v1beta1.IgnoredResource - (*CreateBackupRequest)(nil), // 1: odpf.optimus.core.v1beta1.CreateBackupRequest - (*CreateBackupResponse)(nil), // 2: odpf.optimus.core.v1beta1.CreateBackupResponse - (*ListBackupsRequest)(nil), // 3: odpf.optimus.core.v1beta1.ListBackupsRequest - (*ListBackupsResponse)(nil), // 4: odpf.optimus.core.v1beta1.ListBackupsResponse - (*BackupSpec)(nil), // 5: odpf.optimus.core.v1beta1.BackupSpec - (*GetBackupRequest)(nil), // 6: odpf.optimus.core.v1beta1.GetBackupRequest - (*GetBackupResponse)(nil), // 7: odpf.optimus.core.v1beta1.GetBackupResponse - nil, // 8: odpf.optimus.core.v1beta1.CreateBackupRequest.ConfigEntry - nil, // 9: odpf.optimus.core.v1beta1.BackupSpec.ConfigEntry - (*timestamppb.Timestamp)(nil), // 10: google.protobuf.Timestamp -} -var file_odpf_optimus_core_v1beta1_backup_proto_depIdxs = []int32{ - 8, // 0: odpf.optimus.core.v1beta1.CreateBackupRequest.config:type_name -> odpf.optimus.core.v1beta1.CreateBackupRequest.ConfigEntry - 0, // 1: odpf.optimus.core.v1beta1.CreateBackupResponse.ignored_resources:type_name -> odpf.optimus.core.v1beta1.IgnoredResource - 5, // 2: odpf.optimus.core.v1beta1.ListBackupsResponse.backups:type_name -> odpf.optimus.core.v1beta1.BackupSpec - 10, // 3: odpf.optimus.core.v1beta1.BackupSpec.created_at:type_name -> google.protobuf.Timestamp - 9, // 4: odpf.optimus.core.v1beta1.BackupSpec.config:type_name -> odpf.optimus.core.v1beta1.BackupSpec.ConfigEntry - 5, // 5: odpf.optimus.core.v1beta1.GetBackupResponse.spec:type_name -> odpf.optimus.core.v1beta1.BackupSpec - 1, // 6: odpf.optimus.core.v1beta1.BackupService.CreateBackup:input_type -> odpf.optimus.core.v1beta1.CreateBackupRequest - 3, // 7: odpf.optimus.core.v1beta1.BackupService.ListBackups:input_type -> odpf.optimus.core.v1beta1.ListBackupsRequest - 6, // 8: odpf.optimus.core.v1beta1.BackupService.GetBackup:input_type -> odpf.optimus.core.v1beta1.GetBackupRequest - 2, // 9: odpf.optimus.core.v1beta1.BackupService.CreateBackup:output_type -> odpf.optimus.core.v1beta1.CreateBackupResponse - 4, // 10: odpf.optimus.core.v1beta1.BackupService.ListBackups:output_type -> odpf.optimus.core.v1beta1.ListBackupsResponse - 7, // 11: odpf.optimus.core.v1beta1.BackupService.GetBackup:output_type -> odpf.optimus.core.v1beta1.GetBackupResponse - 9, // [9:12] is the sub-list for method output_type - 6, // [6:9] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name -} - -func init() { file_odpf_optimus_core_v1beta1_backup_proto_init() } -func file_odpf_optimus_core_v1beta1_backup_proto_init() { - if File_odpf_optimus_core_v1beta1_backup_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_odpf_optimus_core_v1beta1_backup_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*IgnoredResource); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_backup_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateBackupRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_backup_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateBackupResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_backup_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListBackupsRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_backup_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListBackupsResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_backup_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BackupSpec); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_backup_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetBackupRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_backup_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetBackupResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_odpf_optimus_core_v1beta1_backup_proto_rawDesc, - NumEnums: 0, - NumMessages: 10, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_odpf_optimus_core_v1beta1_backup_proto_goTypes, - DependencyIndexes: file_odpf_optimus_core_v1beta1_backup_proto_depIdxs, - MessageInfos: file_odpf_optimus_core_v1beta1_backup_proto_msgTypes, - }.Build() - File_odpf_optimus_core_v1beta1_backup_proto = out.File - file_odpf_optimus_core_v1beta1_backup_proto_rawDesc = nil - file_odpf_optimus_core_v1beta1_backup_proto_goTypes = nil - file_odpf_optimus_core_v1beta1_backup_proto_depIdxs = nil -} diff --git a/protos/odpf/optimus/core/v1beta1/job_run.pb.go b/protos/odpf/optimus/core/v1beta1/job_run.pb.go deleted file mode 100644 index 18624c33d7..0000000000 --- a/protos/odpf/optimus/core/v1beta1/job_run.pb.go +++ /dev/null @@ -1,1268 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.0 -// protoc (unknown) -// source: odpf/optimus/core/v1beta1/job_run.proto - -package optimus - -import ( - _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" - _ "google.golang.org/genproto/googleapis/api/annotations" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - durationpb "google.golang.org/protobuf/types/known/durationpb" - timestamppb "google.golang.org/protobuf/types/known/timestamppb" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type InstanceSpec_Type int32 - -const ( - InstanceSpec_TYPE_UNSPECIFIED InstanceSpec_Type = 0 - InstanceSpec_TYPE_TASK InstanceSpec_Type = 1 - InstanceSpec_TYPE_HOOK InstanceSpec_Type = 2 -) - -// Enum value maps for InstanceSpec_Type. -var ( - InstanceSpec_Type_name = map[int32]string{ - 0: "TYPE_UNSPECIFIED", - 1: "TYPE_TASK", - 2: "TYPE_HOOK", - } - InstanceSpec_Type_value = map[string]int32{ - "TYPE_UNSPECIFIED": 0, - "TYPE_TASK": 1, - "TYPE_HOOK": 2, - } -) - -func (x InstanceSpec_Type) Enum() *InstanceSpec_Type { - p := new(InstanceSpec_Type) - *p = x - return p -} - -func (x InstanceSpec_Type) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (InstanceSpec_Type) Descriptor() protoreflect.EnumDescriptor { - return file_odpf_optimus_core_v1beta1_job_run_proto_enumTypes[0].Descriptor() -} - -func (InstanceSpec_Type) Type() protoreflect.EnumType { - return &file_odpf_optimus_core_v1beta1_job_run_proto_enumTypes[0] -} - -func (x InstanceSpec_Type) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use InstanceSpec_Type.Descriptor instead. -func (InstanceSpec_Type) EnumDescriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{7, 0} -} - -// type of data, could be an env var or file -type InstanceSpecData_Type int32 - -const ( - InstanceSpecData_TYPE_UNSPECIFIED InstanceSpecData_Type = 0 - InstanceSpecData_TYPE_ENV InstanceSpecData_Type = 1 - InstanceSpecData_TYPE_FILE InstanceSpecData_Type = 2 -) - -// Enum value maps for InstanceSpecData_Type. -var ( - InstanceSpecData_Type_name = map[int32]string{ - 0: "TYPE_UNSPECIFIED", - 1: "TYPE_ENV", - 2: "TYPE_FILE", - } - InstanceSpecData_Type_value = map[string]int32{ - "TYPE_UNSPECIFIED": 0, - "TYPE_ENV": 1, - "TYPE_FILE": 2, - } -) - -func (x InstanceSpecData_Type) Enum() *InstanceSpecData_Type { - p := new(InstanceSpecData_Type) - *p = x - return p -} - -func (x InstanceSpecData_Type) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (InstanceSpecData_Type) Descriptor() protoreflect.EnumDescriptor { - return file_odpf_optimus_core_v1beta1_job_run_proto_enumTypes[1].Descriptor() -} - -func (InstanceSpecData_Type) Type() protoreflect.EnumType { - return &file_odpf_optimus_core_v1beta1_job_run_proto_enumTypes[1] -} - -func (x InstanceSpecData_Type) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use InstanceSpecData_Type.Descriptor instead. -func (InstanceSpecData_Type) EnumDescriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{8, 0} -} - -type UploadToSchedulerRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` - NamespaceName *string `protobuf:"bytes,2,opt,name=namespace_name,json=namespaceName,proto3,oneof" json:"namespace_name,omitempty"` -} - -func (x *UploadToSchedulerRequest) Reset() { - *x = UploadToSchedulerRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *UploadToSchedulerRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UploadToSchedulerRequest) ProtoMessage() {} - -func (x *UploadToSchedulerRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UploadToSchedulerRequest.ProtoReflect.Descriptor instead. -func (*UploadToSchedulerRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{0} -} - -func (x *UploadToSchedulerRequest) GetProjectName() string { - if x != nil { - return x.ProjectName - } - return "" -} - -func (x *UploadToSchedulerRequest) GetNamespaceName() string { - if x != nil && x.NamespaceName != nil { - return *x.NamespaceName - } - return "" -} - -type UploadToSchedulerResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Status bool `protobuf:"varint,1,opt,name=status,proto3" json:"status,omitempty"` - ErrorMessage string `protobuf:"bytes,2,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"` -} - -func (x *UploadToSchedulerResponse) Reset() { - *x = UploadToSchedulerResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *UploadToSchedulerResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UploadToSchedulerResponse) ProtoMessage() {} - -func (x *UploadToSchedulerResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UploadToSchedulerResponse.ProtoReflect.Descriptor instead. -func (*UploadToSchedulerResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{1} -} - -func (x *UploadToSchedulerResponse) GetStatus() bool { - if x != nil { - return x.Status - } - return false -} - -func (x *UploadToSchedulerResponse) GetErrorMessage() string { - if x != nil { - return x.ErrorMessage - } - return "" -} - -type RegisterJobEventRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` - JobName string `protobuf:"bytes,2,opt,name=job_name,json=jobName,proto3" json:"job_name,omitempty"` - NamespaceName string `protobuf:"bytes,3,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` - Event *JobEvent `protobuf:"bytes,4,opt,name=event,proto3" json:"event,omitempty"` -} - -func (x *RegisterJobEventRequest) Reset() { - *x = RegisterJobEventRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RegisterJobEventRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RegisterJobEventRequest) ProtoMessage() {} - -func (x *RegisterJobEventRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RegisterJobEventRequest.ProtoReflect.Descriptor instead. -func (*RegisterJobEventRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{2} -} - -func (x *RegisterJobEventRequest) GetProjectName() string { - if x != nil { - return x.ProjectName - } - return "" -} - -func (x *RegisterJobEventRequest) GetJobName() string { - if x != nil { - return x.JobName - } - return "" -} - -func (x *RegisterJobEventRequest) GetNamespaceName() string { - if x != nil { - return x.NamespaceName - } - return "" -} - -func (x *RegisterJobEventRequest) GetEvent() *JobEvent { - if x != nil { - return x.Event - } - return nil -} - -type RegisterJobEventResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *RegisterJobEventResponse) Reset() { - *x = RegisterJobEventResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RegisterJobEventResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RegisterJobEventResponse) ProtoMessage() {} - -func (x *RegisterJobEventResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RegisterJobEventResponse.ProtoReflect.Descriptor instead. -func (*RegisterJobEventResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{3} -} - -type JobRunInputRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` - JobName string `protobuf:"bytes,2,opt,name=job_name,json=jobName,proto3" json:"job_name,omitempty"` - ScheduledAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=scheduled_at,json=scheduledAt,proto3" json:"scheduled_at,omitempty"` - InstanceName string `protobuf:"bytes,5,opt,name=instance_name,json=instanceName,proto3" json:"instance_name,omitempty"` - InstanceType InstanceSpec_Type `protobuf:"varint,6,opt,name=instance_type,json=instanceType,proto3,enum=odpf.optimus.core.v1beta1.InstanceSpec_Type" json:"instance_type,omitempty"` - // either set job_name if this is a scheduled execution - // or set jobrun_id if this is a manual triggered execution - // and not really registered as a valid job - JobrunId string `protobuf:"bytes,7,opt,name=jobrun_id,json=jobrunId,proto3" json:"jobrun_id,omitempty"` -} - -func (x *JobRunInputRequest) Reset() { - *x = JobRunInputRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *JobRunInputRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*JobRunInputRequest) ProtoMessage() {} - -func (x *JobRunInputRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use JobRunInputRequest.ProtoReflect.Descriptor instead. -func (*JobRunInputRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{4} -} - -func (x *JobRunInputRequest) GetProjectName() string { - if x != nil { - return x.ProjectName - } - return "" -} - -func (x *JobRunInputRequest) GetJobName() string { - if x != nil { - return x.JobName - } - return "" -} - -func (x *JobRunInputRequest) GetScheduledAt() *timestamppb.Timestamp { - if x != nil { - return x.ScheduledAt - } - return nil -} - -func (x *JobRunInputRequest) GetInstanceName() string { - if x != nil { - return x.InstanceName - } - return "" -} - -func (x *JobRunInputRequest) GetInstanceType() InstanceSpec_Type { - if x != nil { - return x.InstanceType - } - return InstanceSpec_TYPE_UNSPECIFIED -} - -func (x *JobRunInputRequest) GetJobrunId() string { - if x != nil { - return x.JobrunId - } - return "" -} - -type JobRunRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` - JobName string `protobuf:"bytes,2,opt,name=job_name,json=jobName,proto3" json:"job_name,omitempty"` - StartDate *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=start_date,json=startDate,proto3" json:"start_date,omitempty"` - EndDate *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=end_date,json=endDate,proto3" json:"end_date,omitempty"` - Filter []string `protobuf:"bytes,5,rep,name=filter,proto3" json:"filter,omitempty"` -} - -func (x *JobRunRequest) Reset() { - *x = JobRunRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *JobRunRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*JobRunRequest) ProtoMessage() {} - -func (x *JobRunRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use JobRunRequest.ProtoReflect.Descriptor instead. -func (*JobRunRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{5} -} - -func (x *JobRunRequest) GetProjectName() string { - if x != nil { - return x.ProjectName - } - return "" -} - -func (x *JobRunRequest) GetJobName() string { - if x != nil { - return x.JobName - } - return "" -} - -func (x *JobRunRequest) GetStartDate() *timestamppb.Timestamp { - if x != nil { - return x.StartDate - } - return nil -} - -func (x *JobRunRequest) GetEndDate() *timestamppb.Timestamp { - if x != nil { - return x.EndDate - } - return nil -} - -func (x *JobRunRequest) GetFilter() []string { - if x != nil { - return x.Filter - } - return nil -} - -type JobRunResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - JobRuns []*JobRun `protobuf:"bytes,1,rep,name=job_runs,json=jobRuns,proto3" json:"job_runs,omitempty"` -} - -func (x *JobRunResponse) Reset() { - *x = JobRunResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *JobRunResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*JobRunResponse) ProtoMessage() {} - -func (x *JobRunResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use JobRunResponse.ProtoReflect.Descriptor instead. -func (*JobRunResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{6} -} - -func (x *JobRunResponse) GetJobRuns() []*JobRun { - if x != nil { - return x.JobRuns - } - return nil -} - -type InstanceSpec struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - State string `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"` - Data []*InstanceSpecData `protobuf:"bytes,3,rep,name=data,proto3" json:"data,omitempty"` - ExecutedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=executed_at,json=executedAt,proto3" json:"executed_at,omitempty"` - Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"` - Type InstanceSpec_Type `protobuf:"varint,7,opt,name=type,proto3,enum=odpf.optimus.core.v1beta1.InstanceSpec_Type" json:"type,omitempty"` -} - -func (x *InstanceSpec) Reset() { - *x = InstanceSpec{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *InstanceSpec) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*InstanceSpec) ProtoMessage() {} - -func (x *InstanceSpec) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use InstanceSpec.ProtoReflect.Descriptor instead. -func (*InstanceSpec) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{7} -} - -func (x *InstanceSpec) GetState() string { - if x != nil { - return x.State - } - return "" -} - -func (x *InstanceSpec) GetData() []*InstanceSpecData { - if x != nil { - return x.Data - } - return nil -} - -func (x *InstanceSpec) GetExecutedAt() *timestamppb.Timestamp { - if x != nil { - return x.ExecutedAt - } - return nil -} - -func (x *InstanceSpec) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *InstanceSpec) GetType() InstanceSpec_Type { - if x != nil { - return x.Type - } - return InstanceSpec_TYPE_UNSPECIFIED -} - -type InstanceSpecData struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` - Type InstanceSpecData_Type `protobuf:"varint,5,opt,name=type,proto3,enum=odpf.optimus.core.v1beta1.InstanceSpecData_Type" json:"type,omitempty"` -} - -func (x *InstanceSpecData) Reset() { - *x = InstanceSpecData{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *InstanceSpecData) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*InstanceSpecData) ProtoMessage() {} - -func (x *InstanceSpecData) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use InstanceSpecData.ProtoReflect.Descriptor instead. -func (*InstanceSpecData) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{8} -} - -func (x *InstanceSpecData) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *InstanceSpecData) GetValue() string { - if x != nil { - return x.Value - } - return "" -} - -func (x *InstanceSpecData) GetType() InstanceSpecData_Type { - if x != nil { - return x.Type - } - return InstanceSpecData_TYPE_UNSPECIFIED -} - -type JobRunInputResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Envs map[string]string `protobuf:"bytes,1,rep,name=envs,proto3" json:"envs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - Files map[string]string `protobuf:"bytes,2,rep,name=files,proto3" json:"files,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - Secrets map[string]string `protobuf:"bytes,3,rep,name=secrets,proto3" json:"secrets,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` -} - -func (x *JobRunInputResponse) Reset() { - *x = JobRunInputResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *JobRunInputResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*JobRunInputResponse) ProtoMessage() {} - -func (x *JobRunInputResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use JobRunInputResponse.ProtoReflect.Descriptor instead. -func (*JobRunInputResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{9} -} - -func (x *JobRunInputResponse) GetEnvs() map[string]string { - if x != nil { - return x.Envs - } - return nil -} - -func (x *JobRunInputResponse) GetFiles() map[string]string { - if x != nil { - return x.Files - } - return nil -} - -func (x *JobRunInputResponse) GetSecrets() map[string]string { - if x != nil { - return x.Secrets - } - return nil -} - -type TaskWindow struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Size *durationpb.Duration `protobuf:"bytes,1,opt,name=size,proto3" json:"size,omitempty"` - Offset *durationpb.Duration `protobuf:"bytes,2,opt,name=offset,proto3" json:"offset,omitempty"` - TruncateTo string `protobuf:"bytes,3,opt,name=truncate_to,json=truncateTo,proto3" json:"truncate_to,omitempty"` -} - -func (x *TaskWindow) Reset() { - *x = TaskWindow{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TaskWindow) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TaskWindow) ProtoMessage() {} - -func (x *TaskWindow) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[10] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TaskWindow.ProtoReflect.Descriptor instead. -func (*TaskWindow) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{10} -} - -func (x *TaskWindow) GetSize() *durationpb.Duration { - if x != nil { - return x.Size - } - return nil -} - -func (x *TaskWindow) GetOffset() *durationpb.Duration { - if x != nil { - return x.Offset - } - return nil -} - -func (x *TaskWindow) GetTruncateTo() string { - if x != nil { - return x.TruncateTo - } - return "" -} - -var File_odpf_optimus_core_v1beta1_job_run_proto protoreflect.FileDescriptor - -var file_odpf_optimus_core_v1beta1_job_run_proto_rawDesc = []byte{ - 0x0a, 0x27, 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, - 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x6a, 0x6f, 0x62, 0x5f, - 0x72, 0x75, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x6f, 0x64, 0x70, 0x66, 0x2e, - 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, - 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x1a, 0x28, 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, - 0x73, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x6a, - 0x6f, 0x62, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, - 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x7c, 0x0a, - 0x18, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, - 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x0e, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x58, 0x0a, 0x19, 0x55, - 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xb9, 0x01, 0x0a, 0x17, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x4a, 0x6f, 0x62, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, - 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x22, 0x1a, 0x0a, 0x18, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4a, 0x6f, 0x62, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xac, 0x02, - 0x0a, 0x12, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x3d, 0x0a, 0x0c, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x5f, - 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x41, - 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x51, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, - 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0c, 0x69, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6a, 0x6f, 0x62, - 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6a, 0x6f, - 0x62, 0x72, 0x75, 0x6e, 0x49, 0x64, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0xd7, 0x01, 0x0a, - 0x0d, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, - 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x44, 0x61, 0x74, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, - 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, 0x12, 0x16, - 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, - 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x4e, 0x0a, 0x0e, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, - 0x72, 0x75, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6f, 0x64, 0x70, - 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x52, 0x07, 0x6a, - 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x73, 0x22, 0xc0, 0x02, 0x0a, 0x0c, 0x49, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3f, 0x0a, - 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x6f, 0x64, - 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, - 0x53, 0x70, 0x65, 0x63, 0x44, 0x61, 0x74, 0x61, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x3b, - 0x0a, 0x0b, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x0a, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, - 0x40, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, - 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x22, 0x3a, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, - 0x0d, 0x0a, 0x09, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x54, 0x41, 0x53, 0x4b, 0x10, 0x01, 0x12, 0x0d, - 0x0a, 0x09, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x48, 0x4f, 0x4f, 0x4b, 0x10, 0x02, 0x4a, 0x04, 0x08, - 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x22, 0xc9, 0x01, 0x0a, 0x10, 0x49, 0x6e, - 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x44, 0x61, 0x74, 0x61, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x44, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, - 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x44, - 0x61, 0x74, 0x61, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x39, - 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, - 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x4e, 0x56, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, - 0x04, 0x08, 0x04, 0x10, 0x05, 0x22, 0xba, 0x03, 0x0a, 0x13, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, - 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, - 0x04, 0x65, 0x6e, 0x76, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x6f, 0x64, - 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x49, 0x6e, - 0x70, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x45, 0x6e, 0x76, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x65, 0x6e, 0x76, 0x73, 0x12, 0x4f, 0x0a, 0x05, 0x66, - 0x69, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x6f, 0x64, 0x70, - 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x49, 0x6e, 0x70, - 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x55, 0x0a, 0x07, - 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, - 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, - 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x65, - 0x63, 0x72, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x73, 0x65, 0x63, 0x72, - 0x65, 0x74, 0x73, 0x1a, 0x37, 0x0a, 0x09, 0x45, 0x6e, 0x76, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x38, 0x0a, 0x0a, - 0x46, 0x69, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3a, 0x0a, 0x0c, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0x8f, 0x01, 0x0a, 0x0a, 0x54, 0x61, 0x73, 0x6b, 0x57, 0x69, 0x6e, 0x64, 0x6f, - 0x77, 0x12, 0x2d, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, - 0x12, 0x31, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x6f, 0x66, 0x66, - 0x73, 0x65, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x5f, - 0x74, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, - 0x74, 0x65, 0x54, 0x6f, 0x32, 0xed, 0x05, 0x0a, 0x0d, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xb1, 0x01, 0x0a, 0x0b, 0x4a, 0x6f, 0x62, 0x52, 0x75, - 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x2d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, - 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, - 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x43, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3d, 0x22, 0x38, 0x2f, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, - 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6a, - 0x6f, 0x62, 0x2f, 0x7b, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x75, - 0x6e, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0x99, 0x01, 0x0a, 0x06, 0x4a, - 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x12, 0x28, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, - 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x52, - 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3a, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x34, 0x12, 0x32, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x2f, 0x7b, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x7d, 0x2f, 0x72, 0x75, 0x6e, 0x12, 0xd7, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x65, 0x72, 0x4a, 0x6f, 0x62, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x32, 0x2e, 0x6f, 0x64, - 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, - 0x4a, 0x6f, 0x62, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x33, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x65, 0x72, 0x4a, 0x6f, 0x62, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x54, 0x22, 0x4f, 0x2f, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x2f, 0x7b, 0x6a, 0x6f, - 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, - 0x12, 0xb1, 0x01, 0x0a, 0x11, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x53, 0x63, 0x68, - 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x12, 0x33, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, - 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x53, 0x63, 0x68, 0x65, 0x64, - 0x75, 0x6c, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x6f, 0x64, - 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x6f, - 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x1a, 0x26, 0x2f, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x75, 0x70, 0x6c, 0x6f, 0x61, - 0x64, 0x3a, 0x01, 0x2a, 0x42, 0x87, 0x01, 0x0a, 0x16, 0x69, 0x6f, 0x2e, 0x6f, 0x64, 0x70, 0x66, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x42, - 0x0d, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x50, 0x01, - 0x5a, 0x1e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x64, 0x70, - 0x66, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, - 0x92, 0x41, 0x3b, 0x12, 0x05, 0x32, 0x03, 0x30, 0x2e, 0x31, 0x1a, 0x0e, 0x31, 0x32, 0x37, 0x2e, - 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x3a, 0x39, 0x31, 0x30, 0x30, 0x22, 0x04, 0x2f, 0x61, 0x70, 0x69, - 0x2a, 0x01, 0x01, 0x72, 0x19, 0x0a, 0x17, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x20, 0x4a, - 0x6f, 0x62, 0x20, 0x52, 0x75, 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_odpf_optimus_core_v1beta1_job_run_proto_rawDescOnce sync.Once - file_odpf_optimus_core_v1beta1_job_run_proto_rawDescData = file_odpf_optimus_core_v1beta1_job_run_proto_rawDesc -) - -func file_odpf_optimus_core_v1beta1_job_run_proto_rawDescGZIP() []byte { - file_odpf_optimus_core_v1beta1_job_run_proto_rawDescOnce.Do(func() { - file_odpf_optimus_core_v1beta1_job_run_proto_rawDescData = protoimpl.X.CompressGZIP(file_odpf_optimus_core_v1beta1_job_run_proto_rawDescData) - }) - return file_odpf_optimus_core_v1beta1_job_run_proto_rawDescData -} - -var file_odpf_optimus_core_v1beta1_job_run_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes = make([]protoimpl.MessageInfo, 14) -var file_odpf_optimus_core_v1beta1_job_run_proto_goTypes = []interface{}{ - (InstanceSpec_Type)(0), // 0: odpf.optimus.core.v1beta1.InstanceSpec.Type - (InstanceSpecData_Type)(0), // 1: odpf.optimus.core.v1beta1.InstanceSpecData.Type - (*UploadToSchedulerRequest)(nil), // 2: odpf.optimus.core.v1beta1.UploadToSchedulerRequest - (*UploadToSchedulerResponse)(nil), // 3: odpf.optimus.core.v1beta1.UploadToSchedulerResponse - (*RegisterJobEventRequest)(nil), // 4: odpf.optimus.core.v1beta1.RegisterJobEventRequest - (*RegisterJobEventResponse)(nil), // 5: odpf.optimus.core.v1beta1.RegisterJobEventResponse - (*JobRunInputRequest)(nil), // 6: odpf.optimus.core.v1beta1.JobRunInputRequest - (*JobRunRequest)(nil), // 7: odpf.optimus.core.v1beta1.JobRunRequest - (*JobRunResponse)(nil), // 8: odpf.optimus.core.v1beta1.JobRunResponse - (*InstanceSpec)(nil), // 9: odpf.optimus.core.v1beta1.InstanceSpec - (*InstanceSpecData)(nil), // 10: odpf.optimus.core.v1beta1.InstanceSpecData - (*JobRunInputResponse)(nil), // 11: odpf.optimus.core.v1beta1.JobRunInputResponse - (*TaskWindow)(nil), // 12: odpf.optimus.core.v1beta1.TaskWindow - nil, // 13: odpf.optimus.core.v1beta1.JobRunInputResponse.EnvsEntry - nil, // 14: odpf.optimus.core.v1beta1.JobRunInputResponse.FilesEntry - nil, // 15: odpf.optimus.core.v1beta1.JobRunInputResponse.SecretsEntry - (*JobEvent)(nil), // 16: odpf.optimus.core.v1beta1.JobEvent - (*timestamppb.Timestamp)(nil), // 17: google.protobuf.Timestamp - (*JobRun)(nil), // 18: odpf.optimus.core.v1beta1.JobRun - (*durationpb.Duration)(nil), // 19: google.protobuf.Duration -} -var file_odpf_optimus_core_v1beta1_job_run_proto_depIdxs = []int32{ - 16, // 0: odpf.optimus.core.v1beta1.RegisterJobEventRequest.event:type_name -> odpf.optimus.core.v1beta1.JobEvent - 17, // 1: odpf.optimus.core.v1beta1.JobRunInputRequest.scheduled_at:type_name -> google.protobuf.Timestamp - 0, // 2: odpf.optimus.core.v1beta1.JobRunInputRequest.instance_type:type_name -> odpf.optimus.core.v1beta1.InstanceSpec.Type - 17, // 3: odpf.optimus.core.v1beta1.JobRunRequest.start_date:type_name -> google.protobuf.Timestamp - 17, // 4: odpf.optimus.core.v1beta1.JobRunRequest.end_date:type_name -> google.protobuf.Timestamp - 18, // 5: odpf.optimus.core.v1beta1.JobRunResponse.job_runs:type_name -> odpf.optimus.core.v1beta1.JobRun - 10, // 6: odpf.optimus.core.v1beta1.InstanceSpec.data:type_name -> odpf.optimus.core.v1beta1.InstanceSpecData - 17, // 7: odpf.optimus.core.v1beta1.InstanceSpec.executed_at:type_name -> google.protobuf.Timestamp - 0, // 8: odpf.optimus.core.v1beta1.InstanceSpec.type:type_name -> odpf.optimus.core.v1beta1.InstanceSpec.Type - 1, // 9: odpf.optimus.core.v1beta1.InstanceSpecData.type:type_name -> odpf.optimus.core.v1beta1.InstanceSpecData.Type - 13, // 10: odpf.optimus.core.v1beta1.JobRunInputResponse.envs:type_name -> odpf.optimus.core.v1beta1.JobRunInputResponse.EnvsEntry - 14, // 11: odpf.optimus.core.v1beta1.JobRunInputResponse.files:type_name -> odpf.optimus.core.v1beta1.JobRunInputResponse.FilesEntry - 15, // 12: odpf.optimus.core.v1beta1.JobRunInputResponse.secrets:type_name -> odpf.optimus.core.v1beta1.JobRunInputResponse.SecretsEntry - 19, // 13: odpf.optimus.core.v1beta1.TaskWindow.size:type_name -> google.protobuf.Duration - 19, // 14: odpf.optimus.core.v1beta1.TaskWindow.offset:type_name -> google.protobuf.Duration - 6, // 15: odpf.optimus.core.v1beta1.JobRunService.JobRunInput:input_type -> odpf.optimus.core.v1beta1.JobRunInputRequest - 7, // 16: odpf.optimus.core.v1beta1.JobRunService.JobRun:input_type -> odpf.optimus.core.v1beta1.JobRunRequest - 4, // 17: odpf.optimus.core.v1beta1.JobRunService.RegisterJobEvent:input_type -> odpf.optimus.core.v1beta1.RegisterJobEventRequest - 2, // 18: odpf.optimus.core.v1beta1.JobRunService.UploadToScheduler:input_type -> odpf.optimus.core.v1beta1.UploadToSchedulerRequest - 11, // 19: odpf.optimus.core.v1beta1.JobRunService.JobRunInput:output_type -> odpf.optimus.core.v1beta1.JobRunInputResponse - 8, // 20: odpf.optimus.core.v1beta1.JobRunService.JobRun:output_type -> odpf.optimus.core.v1beta1.JobRunResponse - 5, // 21: odpf.optimus.core.v1beta1.JobRunService.RegisterJobEvent:output_type -> odpf.optimus.core.v1beta1.RegisterJobEventResponse - 3, // 22: odpf.optimus.core.v1beta1.JobRunService.UploadToScheduler:output_type -> odpf.optimus.core.v1beta1.UploadToSchedulerResponse - 19, // [19:23] is the sub-list for method output_type - 15, // [15:19] is the sub-list for method input_type - 15, // [15:15] is the sub-list for extension type_name - 15, // [15:15] is the sub-list for extension extendee - 0, // [0:15] is the sub-list for field type_name -} - -func init() { file_odpf_optimus_core_v1beta1_job_run_proto_init() } -func file_odpf_optimus_core_v1beta1_job_run_proto_init() { - if File_odpf_optimus_core_v1beta1_job_run_proto != nil { - return - } - file_odpf_optimus_core_v1beta1_job_spec_proto_init() - if !protoimpl.UnsafeEnabled { - file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UploadToSchedulerRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UploadToSchedulerResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RegisterJobEventRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RegisterJobEventResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*JobRunInputRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*JobRunRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*JobRunResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InstanceSpec); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InstanceSpecData); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*JobRunInputResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TaskWindow); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes[0].OneofWrappers = []interface{}{} - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_odpf_optimus_core_v1beta1_job_run_proto_rawDesc, - NumEnums: 2, - NumMessages: 14, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_odpf_optimus_core_v1beta1_job_run_proto_goTypes, - DependencyIndexes: file_odpf_optimus_core_v1beta1_job_run_proto_depIdxs, - EnumInfos: file_odpf_optimus_core_v1beta1_job_run_proto_enumTypes, - MessageInfos: file_odpf_optimus_core_v1beta1_job_run_proto_msgTypes, - }.Build() - File_odpf_optimus_core_v1beta1_job_run_proto = out.File - file_odpf_optimus_core_v1beta1_job_run_proto_rawDesc = nil - file_odpf_optimus_core_v1beta1_job_run_proto_goTypes = nil - file_odpf_optimus_core_v1beta1_job_run_proto_depIdxs = nil -} diff --git a/protos/odpf/optimus/core/v1beta1/namespace.pb.go b/protos/odpf/optimus/core/v1beta1/namespace.pb.go deleted file mode 100644 index a7d245846b..0000000000 --- a/protos/odpf/optimus/core/v1beta1/namespace.pb.go +++ /dev/null @@ -1,649 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.0 -// protoc (unknown) -// source: odpf/optimus/core/v1beta1/namespace.proto - -package optimus - -import ( - _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" - _ "google.golang.org/genproto/googleapis/api/annotations" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type RegisterProjectNamespaceRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` - Namespace *NamespaceSpecification `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"` -} - -func (x *RegisterProjectNamespaceRequest) Reset() { - *x = RegisterProjectNamespaceRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_namespace_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RegisterProjectNamespaceRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RegisterProjectNamespaceRequest) ProtoMessage() {} - -func (x *RegisterProjectNamespaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_namespace_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RegisterProjectNamespaceRequest.ProtoReflect.Descriptor instead. -func (*RegisterProjectNamespaceRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_namespace_proto_rawDescGZIP(), []int{0} -} - -func (x *RegisterProjectNamespaceRequest) GetProjectName() string { - if x != nil { - return x.ProjectName - } - return "" -} - -func (x *RegisterProjectNamespaceRequest) GetNamespace() *NamespaceSpecification { - if x != nil { - return x.Namespace - } - return nil -} - -type RegisterProjectNamespaceResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` - Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` -} - -func (x *RegisterProjectNamespaceResponse) Reset() { - *x = RegisterProjectNamespaceResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_namespace_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RegisterProjectNamespaceResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RegisterProjectNamespaceResponse) ProtoMessage() {} - -func (x *RegisterProjectNamespaceResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_namespace_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RegisterProjectNamespaceResponse.ProtoReflect.Descriptor instead. -func (*RegisterProjectNamespaceResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_namespace_proto_rawDescGZIP(), []int{1} -} - -func (x *RegisterProjectNamespaceResponse) GetSuccess() bool { - if x != nil { - return x.Success - } - return false -} - -func (x *RegisterProjectNamespaceResponse) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -type ListProjectNamespacesRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` -} - -func (x *ListProjectNamespacesRequest) Reset() { - *x = ListProjectNamespacesRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_namespace_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ListProjectNamespacesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListProjectNamespacesRequest) ProtoMessage() {} - -func (x *ListProjectNamespacesRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_namespace_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListProjectNamespacesRequest.ProtoReflect.Descriptor instead. -func (*ListProjectNamespacesRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_namespace_proto_rawDescGZIP(), []int{2} -} - -func (x *ListProjectNamespacesRequest) GetProjectName() string { - if x != nil { - return x.ProjectName - } - return "" -} - -type ListProjectNamespacesResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Namespaces []*NamespaceSpecification `protobuf:"bytes,1,rep,name=namespaces,proto3" json:"namespaces,omitempty"` -} - -func (x *ListProjectNamespacesResponse) Reset() { - *x = ListProjectNamespacesResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_namespace_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ListProjectNamespacesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListProjectNamespacesResponse) ProtoMessage() {} - -func (x *ListProjectNamespacesResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_namespace_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListProjectNamespacesResponse.ProtoReflect.Descriptor instead. -func (*ListProjectNamespacesResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_namespace_proto_rawDescGZIP(), []int{3} -} - -func (x *ListProjectNamespacesResponse) GetNamespaces() []*NamespaceSpecification { - if x != nil { - return x.Namespaces - } - return nil -} - -type GetNamespaceRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` - NamespaceName string `protobuf:"bytes,2,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` -} - -func (x *GetNamespaceRequest) Reset() { - *x = GetNamespaceRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_namespace_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetNamespaceRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetNamespaceRequest) ProtoMessage() {} - -func (x *GetNamespaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_namespace_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetNamespaceRequest.ProtoReflect.Descriptor instead. -func (*GetNamespaceRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_namespace_proto_rawDescGZIP(), []int{4} -} - -func (x *GetNamespaceRequest) GetProjectName() string { - if x != nil { - return x.ProjectName - } - return "" -} - -func (x *GetNamespaceRequest) GetNamespaceName() string { - if x != nil { - return x.NamespaceName - } - return "" -} - -type GetNamespaceResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Namespace *NamespaceSpecification `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` -} - -func (x *GetNamespaceResponse) Reset() { - *x = GetNamespaceResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_namespace_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetNamespaceResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetNamespaceResponse) ProtoMessage() {} - -func (x *GetNamespaceResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_namespace_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetNamespaceResponse.ProtoReflect.Descriptor instead. -func (*GetNamespaceResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_namespace_proto_rawDescGZIP(), []int{5} -} - -func (x *GetNamespaceResponse) GetNamespace() *NamespaceSpecification { - if x != nil { - return x.Namespace - } - return nil -} - -type NamespaceSpecification struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Config map[string]string `protobuf:"bytes,2,rep,name=config,proto3" json:"config,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` -} - -func (x *NamespaceSpecification) Reset() { - *x = NamespaceSpecification{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_namespace_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *NamespaceSpecification) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*NamespaceSpecification) ProtoMessage() {} - -func (x *NamespaceSpecification) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_namespace_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use NamespaceSpecification.ProtoReflect.Descriptor instead. -func (*NamespaceSpecification) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_namespace_proto_rawDescGZIP(), []int{6} -} - -func (x *NamespaceSpecification) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *NamespaceSpecification) GetConfig() map[string]string { - if x != nil { - return x.Config - } - return nil -} - -var File_odpf_optimus_core_v1beta1_namespace_proto protoreflect.FileDescriptor - -var file_odpf_optimus_core_v1beta1_namespace_proto_rawDesc = []byte{ - 0x0a, 0x29, 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, - 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x6f, 0x64, 0x70, - 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, - 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, - 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x95, 0x01, 0x0a, 0x1f, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, - 0x72, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x4f, 0x0a, 0x09, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, - 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x56, 0x0a, 0x20, - 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x22, 0x41, 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x72, 0x0a, 0x1d, 0x4c, 0x69, 0x73, 0x74, 0x50, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x6f, - 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x22, 0x5f, 0x0a, 0x13, 0x47, - 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x67, 0x0a, 0x14, - 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, - 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x70, 0x65, - 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0xbe, 0x01, 0x0a, 0x16, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x55, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, - 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x39, 0x0a, 0x0b, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0xd4, 0x04, 0x0a, 0x10, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xc9, 0x01, 0x0a, 0x18, - 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x3a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, - 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, - 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x34, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2e, 0x22, 0x29, 0x2f, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0xbd, 0x01, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, - 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x73, 0x12, 0x37, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, - 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, - 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x6f, 0x64, 0x70, - 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x12, 0x29, 0x2f, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0xb3, 0x01, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, - 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, - 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x42, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x3c, 0x12, 0x3a, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x42, 0x93, 0x01, - 0x0a, 0x16, 0x69, 0x6f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, - 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x42, 0x17, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x72, 0x50, 0x01, 0x5a, 0x1e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, 0x6f, 0x70, 0x74, 0x69, - 0x6d, 0x75, 0x73, 0x92, 0x41, 0x3d, 0x12, 0x05, 0x32, 0x03, 0x30, 0x2e, 0x31, 0x1a, 0x0e, 0x31, - 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x3a, 0x39, 0x31, 0x30, 0x30, 0x22, 0x04, 0x2f, - 0x61, 0x70, 0x69, 0x2a, 0x01, 0x01, 0x72, 0x1b, 0x0a, 0x19, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x75, - 0x73, 0x20, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_odpf_optimus_core_v1beta1_namespace_proto_rawDescOnce sync.Once - file_odpf_optimus_core_v1beta1_namespace_proto_rawDescData = file_odpf_optimus_core_v1beta1_namespace_proto_rawDesc -) - -func file_odpf_optimus_core_v1beta1_namespace_proto_rawDescGZIP() []byte { - file_odpf_optimus_core_v1beta1_namespace_proto_rawDescOnce.Do(func() { - file_odpf_optimus_core_v1beta1_namespace_proto_rawDescData = protoimpl.X.CompressGZIP(file_odpf_optimus_core_v1beta1_namespace_proto_rawDescData) - }) - return file_odpf_optimus_core_v1beta1_namespace_proto_rawDescData -} - -var file_odpf_optimus_core_v1beta1_namespace_proto_msgTypes = make([]protoimpl.MessageInfo, 8) -var file_odpf_optimus_core_v1beta1_namespace_proto_goTypes = []interface{}{ - (*RegisterProjectNamespaceRequest)(nil), // 0: odpf.optimus.core.v1beta1.RegisterProjectNamespaceRequest - (*RegisterProjectNamespaceResponse)(nil), // 1: odpf.optimus.core.v1beta1.RegisterProjectNamespaceResponse - (*ListProjectNamespacesRequest)(nil), // 2: odpf.optimus.core.v1beta1.ListProjectNamespacesRequest - (*ListProjectNamespacesResponse)(nil), // 3: odpf.optimus.core.v1beta1.ListProjectNamespacesResponse - (*GetNamespaceRequest)(nil), // 4: odpf.optimus.core.v1beta1.GetNamespaceRequest - (*GetNamespaceResponse)(nil), // 5: odpf.optimus.core.v1beta1.GetNamespaceResponse - (*NamespaceSpecification)(nil), // 6: odpf.optimus.core.v1beta1.NamespaceSpecification - nil, // 7: odpf.optimus.core.v1beta1.NamespaceSpecification.ConfigEntry -} -var file_odpf_optimus_core_v1beta1_namespace_proto_depIdxs = []int32{ - 6, // 0: odpf.optimus.core.v1beta1.RegisterProjectNamespaceRequest.namespace:type_name -> odpf.optimus.core.v1beta1.NamespaceSpecification - 6, // 1: odpf.optimus.core.v1beta1.ListProjectNamespacesResponse.namespaces:type_name -> odpf.optimus.core.v1beta1.NamespaceSpecification - 6, // 2: odpf.optimus.core.v1beta1.GetNamespaceResponse.namespace:type_name -> odpf.optimus.core.v1beta1.NamespaceSpecification - 7, // 3: odpf.optimus.core.v1beta1.NamespaceSpecification.config:type_name -> odpf.optimus.core.v1beta1.NamespaceSpecification.ConfigEntry - 0, // 4: odpf.optimus.core.v1beta1.NamespaceService.RegisterProjectNamespace:input_type -> odpf.optimus.core.v1beta1.RegisterProjectNamespaceRequest - 2, // 5: odpf.optimus.core.v1beta1.NamespaceService.ListProjectNamespaces:input_type -> odpf.optimus.core.v1beta1.ListProjectNamespacesRequest - 4, // 6: odpf.optimus.core.v1beta1.NamespaceService.GetNamespace:input_type -> odpf.optimus.core.v1beta1.GetNamespaceRequest - 1, // 7: odpf.optimus.core.v1beta1.NamespaceService.RegisterProjectNamespace:output_type -> odpf.optimus.core.v1beta1.RegisterProjectNamespaceResponse - 3, // 8: odpf.optimus.core.v1beta1.NamespaceService.ListProjectNamespaces:output_type -> odpf.optimus.core.v1beta1.ListProjectNamespacesResponse - 5, // 9: odpf.optimus.core.v1beta1.NamespaceService.GetNamespace:output_type -> odpf.optimus.core.v1beta1.GetNamespaceResponse - 7, // [7:10] is the sub-list for method output_type - 4, // [4:7] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name -} - -func init() { file_odpf_optimus_core_v1beta1_namespace_proto_init() } -func file_odpf_optimus_core_v1beta1_namespace_proto_init() { - if File_odpf_optimus_core_v1beta1_namespace_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_odpf_optimus_core_v1beta1_namespace_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RegisterProjectNamespaceRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_namespace_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RegisterProjectNamespaceResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_namespace_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListProjectNamespacesRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_namespace_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListProjectNamespacesResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_namespace_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetNamespaceRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_namespace_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetNamespaceResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_namespace_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NamespaceSpecification); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_odpf_optimus_core_v1beta1_namespace_proto_rawDesc, - NumEnums: 0, - NumMessages: 8, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_odpf_optimus_core_v1beta1_namespace_proto_goTypes, - DependencyIndexes: file_odpf_optimus_core_v1beta1_namespace_proto_depIdxs, - MessageInfos: file_odpf_optimus_core_v1beta1_namespace_proto_msgTypes, - }.Build() - File_odpf_optimus_core_v1beta1_namespace_proto = out.File - file_odpf_optimus_core_v1beta1_namespace_proto_rawDesc = nil - file_odpf_optimus_core_v1beta1_namespace_proto_goTypes = nil - file_odpf_optimus_core_v1beta1_namespace_proto_depIdxs = nil -} diff --git a/protos/odpf/optimus/core/v1beta1/project.pb.go b/protos/odpf/optimus/core/v1beta1/project.pb.go deleted file mode 100644 index e2dc743e36..0000000000 --- a/protos/odpf/optimus/core/v1beta1/project.pb.go +++ /dev/null @@ -1,691 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.0 -// protoc (unknown) -// source: odpf/optimus/core/v1beta1/project.proto - -package optimus - -import ( - _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" - _ "google.golang.org/genproto/googleapis/api/annotations" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type RegisterProjectRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Project *ProjectSpecification `protobuf:"bytes,1,opt,name=project,proto3" json:"project,omitempty"` -} - -func (x *RegisterProjectRequest) Reset() { - *x = RegisterProjectRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_project_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RegisterProjectRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RegisterProjectRequest) ProtoMessage() {} - -func (x *RegisterProjectRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_project_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RegisterProjectRequest.ProtoReflect.Descriptor instead. -func (*RegisterProjectRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_project_proto_rawDescGZIP(), []int{0} -} - -func (x *RegisterProjectRequest) GetProject() *ProjectSpecification { - if x != nil { - return x.Project - } - return nil -} - -type RegisterProjectResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` - Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` -} - -func (x *RegisterProjectResponse) Reset() { - *x = RegisterProjectResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_project_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RegisterProjectResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RegisterProjectResponse) ProtoMessage() {} - -func (x *RegisterProjectResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_project_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RegisterProjectResponse.ProtoReflect.Descriptor instead. -func (*RegisterProjectResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_project_proto_rawDescGZIP(), []int{1} -} - -func (x *RegisterProjectResponse) GetSuccess() bool { - if x != nil { - return x.Success - } - return false -} - -func (x *RegisterProjectResponse) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -type ListProjectsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *ListProjectsRequest) Reset() { - *x = ListProjectsRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_project_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ListProjectsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListProjectsRequest) ProtoMessage() {} - -func (x *ListProjectsRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_project_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListProjectsRequest.ProtoReflect.Descriptor instead. -func (*ListProjectsRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_project_proto_rawDescGZIP(), []int{2} -} - -type ListProjectsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Projects []*ProjectSpecification `protobuf:"bytes,1,rep,name=projects,proto3" json:"projects,omitempty"` -} - -func (x *ListProjectsResponse) Reset() { - *x = ListProjectsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_project_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ListProjectsResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListProjectsResponse) ProtoMessage() {} - -func (x *ListProjectsResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_project_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListProjectsResponse.ProtoReflect.Descriptor instead. -func (*ListProjectsResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_project_proto_rawDescGZIP(), []int{3} -} - -func (x *ListProjectsResponse) GetProjects() []*ProjectSpecification { - if x != nil { - return x.Projects - } - return nil -} - -type GetProjectRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` -} - -func (x *GetProjectRequest) Reset() { - *x = GetProjectRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_project_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetProjectRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetProjectRequest) ProtoMessage() {} - -func (x *GetProjectRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_project_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetProjectRequest.ProtoReflect.Descriptor instead. -func (*GetProjectRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_project_proto_rawDescGZIP(), []int{4} -} - -func (x *GetProjectRequest) GetProjectName() string { - if x != nil { - return x.ProjectName - } - return "" -} - -type GetProjectResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Project *ProjectSpecification `protobuf:"bytes,1,opt,name=project,proto3" json:"project,omitempty"` -} - -func (x *GetProjectResponse) Reset() { - *x = GetProjectResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_project_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetProjectResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetProjectResponse) ProtoMessage() {} - -func (x *GetProjectResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_project_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetProjectResponse.ProtoReflect.Descriptor instead. -func (*GetProjectResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_project_proto_rawDescGZIP(), []int{5} -} - -func (x *GetProjectResponse) GetProject() *ProjectSpecification { - if x != nil { - return x.Project - } - return nil -} - -type ProjectSpecification struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Config map[string]string `protobuf:"bytes,2,rep,name=config,proto3" json:"config,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - Secrets []*ProjectSpecification_ProjectSecret `protobuf:"bytes,3,rep,name=secrets,proto3" json:"secrets,omitempty"` -} - -func (x *ProjectSpecification) Reset() { - *x = ProjectSpecification{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_project_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ProjectSpecification) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProjectSpecification) ProtoMessage() {} - -func (x *ProjectSpecification) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_project_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProjectSpecification.ProtoReflect.Descriptor instead. -func (*ProjectSpecification) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_project_proto_rawDescGZIP(), []int{6} -} - -func (x *ProjectSpecification) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *ProjectSpecification) GetConfig() map[string]string { - if x != nil { - return x.Config - } - return nil -} - -func (x *ProjectSpecification) GetSecrets() []*ProjectSpecification_ProjectSecret { - if x != nil { - return x.Secrets - } - return nil -} - -type ProjectSpecification_ProjectSecret struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` -} - -func (x *ProjectSpecification_ProjectSecret) Reset() { - *x = ProjectSpecification_ProjectSecret{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_project_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ProjectSpecification_ProjectSecret) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProjectSpecification_ProjectSecret) ProtoMessage() {} - -func (x *ProjectSpecification_ProjectSecret) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_project_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProjectSpecification_ProjectSecret.ProtoReflect.Descriptor instead. -func (*ProjectSpecification_ProjectSecret) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_project_proto_rawDescGZIP(), []int{6, 1} -} - -func (x *ProjectSpecification_ProjectSecret) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *ProjectSpecification_ProjectSecret) GetValue() string { - if x != nil { - return x.Value - } - return "" -} - -var File_odpf_optimus_core_v1beta1_project_proto protoreflect.FileDescriptor - -var file_odpf_optimus_core_v1beta1_project_proto_rawDesc = []byte{ - 0x0a, 0x27, 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, - 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x6f, 0x64, 0x70, 0x66, 0x2e, - 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, - 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, - 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0x69, 0x0a, 0x16, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x49, 0x0a, 0x07, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, - 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0x4d, 0x0a, - 0x17, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x15, 0x0a, 0x13, - 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x22, 0x63, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x08, 0x70, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, - 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x22, 0x36, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x50, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, - 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, - 0x22, 0x5f, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, - 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x22, 0xce, 0x02, 0x0a, 0x14, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x70, 0x65, - 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x53, - 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, - 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x12, 0x57, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, - 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x52, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x1a, 0x39, 0x0a, 0x0b, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x39, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x32, 0xc9, 0x03, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x95, 0x01, 0x0a, 0x0f, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x31, 0x2e, 0x6f, 0x64, 0x70, 0x66, - 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x6f, - 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, - 0x72, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0x89, 0x01, - 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x2e, - 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, - 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x92, 0x01, 0x0a, 0x0a, 0x47, 0x65, - 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x2c, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, - 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, - 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x27, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x21, 0x12, 0x1f, 0x2f, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, - 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x42, 0x8f, - 0x01, 0x0a, 0x16, 0x69, 0x6f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x6e, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x42, 0x15, 0x50, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, - 0x50, 0x01, 0x5a, 0x1e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, - 0x64, 0x70, 0x66, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, - 0x75, 0x73, 0x92, 0x41, 0x3b, 0x12, 0x05, 0x32, 0x03, 0x30, 0x2e, 0x31, 0x1a, 0x0e, 0x31, 0x32, - 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x3a, 0x39, 0x31, 0x30, 0x30, 0x22, 0x04, 0x2f, 0x61, - 0x70, 0x69, 0x2a, 0x01, 0x01, 0x72, 0x19, 0x0a, 0x17, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, - 0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_odpf_optimus_core_v1beta1_project_proto_rawDescOnce sync.Once - file_odpf_optimus_core_v1beta1_project_proto_rawDescData = file_odpf_optimus_core_v1beta1_project_proto_rawDesc -) - -func file_odpf_optimus_core_v1beta1_project_proto_rawDescGZIP() []byte { - file_odpf_optimus_core_v1beta1_project_proto_rawDescOnce.Do(func() { - file_odpf_optimus_core_v1beta1_project_proto_rawDescData = protoimpl.X.CompressGZIP(file_odpf_optimus_core_v1beta1_project_proto_rawDescData) - }) - return file_odpf_optimus_core_v1beta1_project_proto_rawDescData -} - -var file_odpf_optimus_core_v1beta1_project_proto_msgTypes = make([]protoimpl.MessageInfo, 9) -var file_odpf_optimus_core_v1beta1_project_proto_goTypes = []interface{}{ - (*RegisterProjectRequest)(nil), // 0: odpf.optimus.core.v1beta1.RegisterProjectRequest - (*RegisterProjectResponse)(nil), // 1: odpf.optimus.core.v1beta1.RegisterProjectResponse - (*ListProjectsRequest)(nil), // 2: odpf.optimus.core.v1beta1.ListProjectsRequest - (*ListProjectsResponse)(nil), // 3: odpf.optimus.core.v1beta1.ListProjectsResponse - (*GetProjectRequest)(nil), // 4: odpf.optimus.core.v1beta1.GetProjectRequest - (*GetProjectResponse)(nil), // 5: odpf.optimus.core.v1beta1.GetProjectResponse - (*ProjectSpecification)(nil), // 6: odpf.optimus.core.v1beta1.ProjectSpecification - nil, // 7: odpf.optimus.core.v1beta1.ProjectSpecification.ConfigEntry - (*ProjectSpecification_ProjectSecret)(nil), // 8: odpf.optimus.core.v1beta1.ProjectSpecification.ProjectSecret -} -var file_odpf_optimus_core_v1beta1_project_proto_depIdxs = []int32{ - 6, // 0: odpf.optimus.core.v1beta1.RegisterProjectRequest.project:type_name -> odpf.optimus.core.v1beta1.ProjectSpecification - 6, // 1: odpf.optimus.core.v1beta1.ListProjectsResponse.projects:type_name -> odpf.optimus.core.v1beta1.ProjectSpecification - 6, // 2: odpf.optimus.core.v1beta1.GetProjectResponse.project:type_name -> odpf.optimus.core.v1beta1.ProjectSpecification - 7, // 3: odpf.optimus.core.v1beta1.ProjectSpecification.config:type_name -> odpf.optimus.core.v1beta1.ProjectSpecification.ConfigEntry - 8, // 4: odpf.optimus.core.v1beta1.ProjectSpecification.secrets:type_name -> odpf.optimus.core.v1beta1.ProjectSpecification.ProjectSecret - 0, // 5: odpf.optimus.core.v1beta1.ProjectService.RegisterProject:input_type -> odpf.optimus.core.v1beta1.RegisterProjectRequest - 2, // 6: odpf.optimus.core.v1beta1.ProjectService.ListProjects:input_type -> odpf.optimus.core.v1beta1.ListProjectsRequest - 4, // 7: odpf.optimus.core.v1beta1.ProjectService.GetProject:input_type -> odpf.optimus.core.v1beta1.GetProjectRequest - 1, // 8: odpf.optimus.core.v1beta1.ProjectService.RegisterProject:output_type -> odpf.optimus.core.v1beta1.RegisterProjectResponse - 3, // 9: odpf.optimus.core.v1beta1.ProjectService.ListProjects:output_type -> odpf.optimus.core.v1beta1.ListProjectsResponse - 5, // 10: odpf.optimus.core.v1beta1.ProjectService.GetProject:output_type -> odpf.optimus.core.v1beta1.GetProjectResponse - 8, // [8:11] is the sub-list for method output_type - 5, // [5:8] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name -} - -func init() { file_odpf_optimus_core_v1beta1_project_proto_init() } -func file_odpf_optimus_core_v1beta1_project_proto_init() { - if File_odpf_optimus_core_v1beta1_project_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_odpf_optimus_core_v1beta1_project_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RegisterProjectRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_project_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RegisterProjectResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_project_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListProjectsRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_project_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListProjectsResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_project_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetProjectRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_project_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetProjectResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_project_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProjectSpecification); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_project_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProjectSpecification_ProjectSecret); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_odpf_optimus_core_v1beta1_project_proto_rawDesc, - NumEnums: 0, - NumMessages: 9, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_odpf_optimus_core_v1beta1_project_proto_goTypes, - DependencyIndexes: file_odpf_optimus_core_v1beta1_project_proto_depIdxs, - MessageInfos: file_odpf_optimus_core_v1beta1_project_proto_msgTypes, - }.Build() - File_odpf_optimus_core_v1beta1_project_proto = out.File - file_odpf_optimus_core_v1beta1_project_proto_rawDesc = nil - file_odpf_optimus_core_v1beta1_project_proto_goTypes = nil - file_odpf_optimus_core_v1beta1_project_proto_depIdxs = nil -} diff --git a/protos/odpf/optimus/core/v1beta1/replay.pb.go b/protos/odpf/optimus/core/v1beta1/replay.pb.go deleted file mode 100644 index 9604a6e3f0..0000000000 --- a/protos/odpf/optimus/core/v1beta1/replay.pb.go +++ /dev/null @@ -1,314 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.0 -// protoc (unknown) -// source: odpf/optimus/core/v1beta1/replay.proto - -package optimus - -import ( - _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" - _ "google.golang.org/genproto/googleapis/api/annotations" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - timestamppb "google.golang.org/protobuf/types/known/timestamppb" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type ReplayRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` - JobName string `protobuf:"bytes,2,opt,name=job_name,json=jobName,proto3" json:"job_name,omitempty"` - NamespaceName string `protobuf:"bytes,3,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` - StartTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` - EndTime *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` - Parallel bool `protobuf:"varint,6,opt,name=parallel,proto3" json:"parallel,omitempty"` - Description string `protobuf:"bytes,7,opt,name=description,proto3" json:"description,omitempty"` - JobConfig string `protobuf:"bytes,8,opt,name=job_config,json=jobConfig,proto3" json:"job_config,omitempty"` -} - -func (x *ReplayRequest) Reset() { - *x = ReplayRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_replay_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ReplayRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReplayRequest) ProtoMessage() {} - -func (x *ReplayRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_replay_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ReplayRequest.ProtoReflect.Descriptor instead. -func (*ReplayRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_replay_proto_rawDescGZIP(), []int{0} -} - -func (x *ReplayRequest) GetProjectName() string { - if x != nil { - return x.ProjectName - } - return "" -} - -func (x *ReplayRequest) GetJobName() string { - if x != nil { - return x.JobName - } - return "" -} - -func (x *ReplayRequest) GetNamespaceName() string { - if x != nil { - return x.NamespaceName - } - return "" -} - -func (x *ReplayRequest) GetStartTime() *timestamppb.Timestamp { - if x != nil { - return x.StartTime - } - return nil -} - -func (x *ReplayRequest) GetEndTime() *timestamppb.Timestamp { - if x != nil { - return x.EndTime - } - return nil -} - -func (x *ReplayRequest) GetParallel() bool { - if x != nil { - return x.Parallel - } - return false -} - -func (x *ReplayRequest) GetDescription() string { - if x != nil { - return x.Description - } - return "" -} - -func (x *ReplayRequest) GetJobConfig() string { - if x != nil { - return x.JobConfig - } - return "" -} - -type ReplayResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` -} - -func (x *ReplayResponse) Reset() { - *x = ReplayResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_replay_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ReplayResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReplayResponse) ProtoMessage() {} - -func (x *ReplayResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_replay_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ReplayResponse.ProtoReflect.Descriptor instead. -func (*ReplayResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_replay_proto_rawDescGZIP(), []int{1} -} - -func (x *ReplayResponse) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -var File_odpf_optimus_core_v1beta1_replay_proto protoreflect.FileDescriptor - -var file_odpf_optimus_core_v1beta1_replay_proto_rawDesc = []byte{ - 0x0a, 0x26, 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, - 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x72, 0x65, 0x70, 0x6c, - 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, - 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, - 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, - 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0xc3, 0x02, 0x0a, 0x0d, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, - 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x70, - 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x6a, 0x6f, 0x62, - 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6a, - 0x6f, 0x62, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x20, 0x0a, 0x0e, 0x52, 0x65, 0x70, 0x6c, - 0x61, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x32, 0xa2, 0x01, 0x0a, 0x0d, 0x52, - 0x65, 0x70, 0x6c, 0x61, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x90, 0x01, 0x0a, - 0x06, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x12, 0x28, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, - 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, - 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, - 0x70, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x2b, 0x22, 0x26, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x01, 0x2a, 0x42, - 0x8d, 0x01, 0x0a, 0x16, 0x69, 0x6f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x6e, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x42, 0x14, 0x52, 0x65, 0x70, 0x6c, - 0x61, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, - 0x50, 0x01, 0x5a, 0x1e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, - 0x64, 0x70, 0x66, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, - 0x75, 0x73, 0x92, 0x41, 0x3a, 0x12, 0x05, 0x32, 0x03, 0x30, 0x2e, 0x31, 0x1a, 0x0e, 0x31, 0x32, - 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x3a, 0x39, 0x31, 0x30, 0x30, 0x22, 0x04, 0x2f, 0x61, - 0x70, 0x69, 0x2a, 0x01, 0x01, 0x72, 0x18, 0x0a, 0x16, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, - 0x20, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_odpf_optimus_core_v1beta1_replay_proto_rawDescOnce sync.Once - file_odpf_optimus_core_v1beta1_replay_proto_rawDescData = file_odpf_optimus_core_v1beta1_replay_proto_rawDesc -) - -func file_odpf_optimus_core_v1beta1_replay_proto_rawDescGZIP() []byte { - file_odpf_optimus_core_v1beta1_replay_proto_rawDescOnce.Do(func() { - file_odpf_optimus_core_v1beta1_replay_proto_rawDescData = protoimpl.X.CompressGZIP(file_odpf_optimus_core_v1beta1_replay_proto_rawDescData) - }) - return file_odpf_optimus_core_v1beta1_replay_proto_rawDescData -} - -var file_odpf_optimus_core_v1beta1_replay_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_odpf_optimus_core_v1beta1_replay_proto_goTypes = []interface{}{ - (*ReplayRequest)(nil), // 0: odpf.optimus.core.v1beta1.ReplayRequest - (*ReplayResponse)(nil), // 1: odpf.optimus.core.v1beta1.ReplayResponse - (*timestamppb.Timestamp)(nil), // 2: google.protobuf.Timestamp -} -var file_odpf_optimus_core_v1beta1_replay_proto_depIdxs = []int32{ - 2, // 0: odpf.optimus.core.v1beta1.ReplayRequest.start_time:type_name -> google.protobuf.Timestamp - 2, // 1: odpf.optimus.core.v1beta1.ReplayRequest.end_time:type_name -> google.protobuf.Timestamp - 0, // 2: odpf.optimus.core.v1beta1.ReplayService.Replay:input_type -> odpf.optimus.core.v1beta1.ReplayRequest - 1, // 3: odpf.optimus.core.v1beta1.ReplayService.Replay:output_type -> odpf.optimus.core.v1beta1.ReplayResponse - 3, // [3:4] is the sub-list for method output_type - 2, // [2:3] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name -} - -func init() { file_odpf_optimus_core_v1beta1_replay_proto_init() } -func file_odpf_optimus_core_v1beta1_replay_proto_init() { - if File_odpf_optimus_core_v1beta1_replay_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_odpf_optimus_core_v1beta1_replay_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReplayRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_replay_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReplayResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_odpf_optimus_core_v1beta1_replay_proto_rawDesc, - NumEnums: 0, - NumMessages: 2, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_odpf_optimus_core_v1beta1_replay_proto_goTypes, - DependencyIndexes: file_odpf_optimus_core_v1beta1_replay_proto_depIdxs, - MessageInfos: file_odpf_optimus_core_v1beta1_replay_proto_msgTypes, - }.Build() - File_odpf_optimus_core_v1beta1_replay_proto = out.File - file_odpf_optimus_core_v1beta1_replay_proto_rawDesc = nil - file_odpf_optimus_core_v1beta1_replay_proto_goTypes = nil - file_odpf_optimus_core_v1beta1_replay_proto_depIdxs = nil -} diff --git a/protos/odpf/optimus/core/v1beta1/replay.pb.gw.go b/protos/odpf/optimus/core/v1beta1/replay.pb.gw.go deleted file mode 100644 index 9d75015115..0000000000 --- a/protos/odpf/optimus/core/v1beta1/replay.pb.gw.go +++ /dev/null @@ -1,201 +0,0 @@ -// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. -// source: odpf/optimus/core/v1beta1/replay.proto - -/* -Package optimus is a reverse proxy. - -It translates gRPC into RESTful JSON APIs. -*/ -package optimus - -import ( - "context" - "io" - "net/http" - - "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" - "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" - "google.golang.org/protobuf/proto" -) - -// Suppress "imported and not used" errors -var _ codes.Code -var _ io.Reader -var _ status.Status -var _ = runtime.String -var _ = utilities.NewDoubleArray -var _ = metadata.Join - -func request_ReplayService_Replay_0(ctx context.Context, marshaler runtime.Marshaler, client ReplayServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq ReplayRequest - 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); 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["project_name"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "project_name") - } - - protoReq.ProjectName, err = runtime.String(val) - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "project_name", err) - } - - msg, err := client.Replay(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func local_request_ReplayService_Replay_0(ctx context.Context, marshaler runtime.Marshaler, server ReplayServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq ReplayRequest - 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); 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["project_name"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "project_name") - } - - protoReq.ProjectName, err = runtime.String(val) - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "project_name", err) - } - - msg, err := server.Replay(ctx, &protoReq) - return msg, metadata, err - -} - -// RegisterReplayServiceHandlerServer registers the http handlers for service ReplayService to "mux". -// UnaryRPC :call ReplayServiceServer directly. -// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterReplayServiceHandlerFromEndpoint instead. -func RegisterReplayServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server ReplayServiceServer) error { - - mux.Handle("POST", pattern_ReplayService_Replay_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - var stream runtime.ServerTransportStream - ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.ReplayService/Replay", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/replay")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_ReplayService_Replay_0(rctx, inboundMarshaler, server, req, pathParams) - md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_ReplayService_Replay_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - return nil -} - -// RegisterReplayServiceHandlerFromEndpoint is same as RegisterReplayServiceHandler but -// automatically dials to "endpoint" and closes the connection when "ctx" gets done. -func RegisterReplayServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { - conn, err := grpc.Dial(endpoint, opts...) - if err != nil { - return err - } - defer func() { - if err != nil { - if cerr := conn.Close(); cerr != nil { - grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) - } - return - } - go func() { - <-ctx.Done() - if cerr := conn.Close(); cerr != nil { - grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) - } - }() - }() - - return RegisterReplayServiceHandler(ctx, mux, conn) -} - -// RegisterReplayServiceHandler registers the http handlers for service ReplayService to "mux". -// The handlers forward requests to the grpc endpoint over "conn". -func RegisterReplayServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { - return RegisterReplayServiceHandlerClient(ctx, mux, NewReplayServiceClient(conn)) -} - -// RegisterReplayServiceHandlerClient registers the http handlers for service ReplayService -// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "ReplayServiceClient". -// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "ReplayServiceClient" -// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in -// "ReplayServiceClient" to call the correct interceptors. -func RegisterReplayServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client ReplayServiceClient) error { - - mux.Handle("POST", pattern_ReplayService_Replay_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, "/odpf.optimus.core.v1beta1.ReplayService/Replay", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/replay")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_ReplayService_Replay_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_ReplayService_Replay_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - return nil -} - -var ( - pattern_ReplayService_Replay_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"v1beta1", "project", "project_name", "replay"}, "")) -) - -var ( - forward_ReplayService_Replay_0 = runtime.ForwardResponseMessage -) diff --git a/protos/odpf/optimus/core/v1beta1/replay.swagger.json b/protos/odpf/optimus/core/v1beta1/replay.swagger.json deleted file mode 100644 index 51651331e6..0000000000 --- a/protos/odpf/optimus/core/v1beta1/replay.swagger.json +++ /dev/null @@ -1,131 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "title": "odpf/optimus/core/v1beta1/replay.proto", - "version": "0.1" - }, - "tags": [ - { - "name": "ReplayService" - } - ], - "host": "127.0.0.1:9100", - "basePath": "/api", - "schemes": [ - "http" - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "paths": { - "/v1beta1/project/{projectName}/replay": { - "post": { - "operationId": "ReplayService_Replay", - "responses": { - "200": { - "description": "A successful response.", - "schema": { - "$ref": "#/definitions/v1beta1ReplayResponse" - } - }, - "default": { - "description": "An unexpected error response.", - "schema": { - "$ref": "#/definitions/rpcStatus" - } - } - }, - "parameters": [ - { - "name": "projectName", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "properties": { - "jobName": { - "type": "string" - }, - "namespaceName": { - "type": "string" - }, - "startTime": { - "type": "string", - "format": "date-time" - }, - "endTime": { - "type": "string", - "format": "date-time" - }, - "parallel": { - "type": "boolean" - }, - "description": { - "type": "string" - }, - "jobConfig": { - "type": "string" - } - } - } - } - ], - "tags": [ - "ReplayService" - ] - } - } - }, - "definitions": { - "protobufAny": { - "type": "object", - "properties": { - "typeUrl": { - "type": "string" - }, - "value": { - "type": "string", - "format": "byte" - } - } - }, - "rpcStatus": { - "type": "object", - "properties": { - "code": { - "type": "integer", - "format": "int32" - }, - "message": { - "type": "string" - }, - "details": { - "type": "array", - "items": { - "$ref": "#/definitions/protobufAny" - } - } - } - }, - "v1beta1ReplayResponse": { - "type": "object", - "properties": { - "id": { - "type": "string" - } - } - } - }, - "externalDocs": { - "description": "Optimus Replay Service" - } -} diff --git a/protos/odpf/optimus/core/v1beta1/replay_grpc.pb.go b/protos/odpf/optimus/core/v1beta1/replay_grpc.pb.go deleted file mode 100644 index cc9b7a2cbe..0000000000 --- a/protos/odpf/optimus/core/v1beta1/replay_grpc.pb.go +++ /dev/null @@ -1,105 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.2.0 -// - protoc (unknown) -// source: odpf/optimus/core/v1beta1/replay.proto - -package optimus - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 - -// ReplayServiceClient is the client API for ReplayService service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type ReplayServiceClient interface { - Replay(ctx context.Context, in *ReplayRequest, opts ...grpc.CallOption) (*ReplayResponse, error) -} - -type replayServiceClient struct { - cc grpc.ClientConnInterface -} - -func NewReplayServiceClient(cc grpc.ClientConnInterface) ReplayServiceClient { - return &replayServiceClient{cc} -} - -func (c *replayServiceClient) Replay(ctx context.Context, in *ReplayRequest, opts ...grpc.CallOption) (*ReplayResponse, error) { - out := new(ReplayResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.ReplayService/Replay", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// ReplayServiceServer is the server API for ReplayService service. -// All implementations must embed UnimplementedReplayServiceServer -// for forward compatibility -type ReplayServiceServer interface { - Replay(context.Context, *ReplayRequest) (*ReplayResponse, error) - mustEmbedUnimplementedReplayServiceServer() -} - -// UnimplementedReplayServiceServer must be embedded to have forward compatible implementations. -type UnimplementedReplayServiceServer struct { -} - -func (UnimplementedReplayServiceServer) Replay(context.Context, *ReplayRequest) (*ReplayResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Replay not implemented") -} -func (UnimplementedReplayServiceServer) mustEmbedUnimplementedReplayServiceServer() {} - -// UnsafeReplayServiceServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to ReplayServiceServer will -// result in compilation errors. -type UnsafeReplayServiceServer interface { - mustEmbedUnimplementedReplayServiceServer() -} - -func RegisterReplayServiceServer(s grpc.ServiceRegistrar, srv ReplayServiceServer) { - s.RegisterService(&ReplayService_ServiceDesc, srv) -} - -func _ReplayService_Replay_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ReplayRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ReplayServiceServer).Replay(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.ReplayService/Replay", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ReplayServiceServer).Replay(ctx, req.(*ReplayRequest)) - } - return interceptor(ctx, in, info, handler) -} - -// ReplayService_ServiceDesc is the grpc.ServiceDesc for ReplayService service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var ReplayService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "odpf.optimus.core.v1beta1.ReplayService", - HandlerType: (*ReplayServiceServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Replay", - Handler: _ReplayService_Replay_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "odpf/optimus/core/v1beta1/replay.proto", -} diff --git a/protos/odpf/optimus/core/v1beta1/resource.pb.go b/protos/odpf/optimus/core/v1beta1/resource.pb.go deleted file mode 100644 index f552f76677..0000000000 --- a/protos/odpf/optimus/core/v1beta1/resource.pb.go +++ /dev/null @@ -1,1167 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.0 -// protoc (unknown) -// source: odpf/optimus/core/v1beta1/resource.proto - -package optimus - -import ( - _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" - _ "google.golang.org/genproto/googleapis/api/annotations" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - structpb "google.golang.org/protobuf/types/known/structpb" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type DeployResourceSpecificationRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` - DatastoreName string `protobuf:"bytes,2,opt,name=datastore_name,json=datastoreName,proto3" json:"datastore_name,omitempty"` - Resources []*ResourceSpecification `protobuf:"bytes,3,rep,name=resources,proto3" json:"resources,omitempty"` - NamespaceName string `protobuf:"bytes,4,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` -} - -func (x *DeployResourceSpecificationRequest) Reset() { - *x = DeployResourceSpecificationRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DeployResourceSpecificationRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeployResourceSpecificationRequest) ProtoMessage() {} - -func (x *DeployResourceSpecificationRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeployResourceSpecificationRequest.ProtoReflect.Descriptor instead. -func (*DeployResourceSpecificationRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{0} -} - -func (x *DeployResourceSpecificationRequest) GetProjectName() string { - if x != nil { - return x.ProjectName - } - return "" -} - -func (x *DeployResourceSpecificationRequest) GetDatastoreName() string { - if x != nil { - return x.DatastoreName - } - return "" -} - -func (x *DeployResourceSpecificationRequest) GetResources() []*ResourceSpecification { - if x != nil { - return x.Resources - } - return nil -} - -func (x *DeployResourceSpecificationRequest) GetNamespaceName() string { - if x != nil { - return x.NamespaceName - } - return "" -} - -type DeployResourceSpecificationResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - LogStatus *Log `protobuf:"bytes,5,opt,name=log_status,json=logStatus,proto3" json:"log_status,omitempty"` -} - -func (x *DeployResourceSpecificationResponse) Reset() { - *x = DeployResourceSpecificationResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DeployResourceSpecificationResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeployResourceSpecificationResponse) ProtoMessage() {} - -func (x *DeployResourceSpecificationResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeployResourceSpecificationResponse.ProtoReflect.Descriptor instead. -func (*DeployResourceSpecificationResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{1} -} - -func (x *DeployResourceSpecificationResponse) GetLogStatus() *Log { - if x != nil { - return x.LogStatus - } - return nil -} - -// ListResourceSpecificationRequest lists all resource specifications of a datastore in project -type ListResourceSpecificationRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` - DatastoreName string `protobuf:"bytes,2,opt,name=datastore_name,json=datastoreName,proto3" json:"datastore_name,omitempty"` - NamespaceName string `protobuf:"bytes,3,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` -} - -func (x *ListResourceSpecificationRequest) Reset() { - *x = ListResourceSpecificationRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ListResourceSpecificationRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListResourceSpecificationRequest) ProtoMessage() {} - -func (x *ListResourceSpecificationRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListResourceSpecificationRequest.ProtoReflect.Descriptor instead. -func (*ListResourceSpecificationRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{2} -} - -func (x *ListResourceSpecificationRequest) GetProjectName() string { - if x != nil { - return x.ProjectName - } - return "" -} - -func (x *ListResourceSpecificationRequest) GetDatastoreName() string { - if x != nil { - return x.DatastoreName - } - return "" -} - -func (x *ListResourceSpecificationRequest) GetNamespaceName() string { - if x != nil { - return x.NamespaceName - } - return "" -} - -type ListResourceSpecificationResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Resources []*ResourceSpecification `protobuf:"bytes,1,rep,name=resources,proto3" json:"resources,omitempty"` -} - -func (x *ListResourceSpecificationResponse) Reset() { - *x = ListResourceSpecificationResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ListResourceSpecificationResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListResourceSpecificationResponse) ProtoMessage() {} - -func (x *ListResourceSpecificationResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListResourceSpecificationResponse.ProtoReflect.Descriptor instead. -func (*ListResourceSpecificationResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{3} -} - -func (x *ListResourceSpecificationResponse) GetResources() []*ResourceSpecification { - if x != nil { - return x.Resources - } - return nil -} - -type CreateResourceRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` - DatastoreName string `protobuf:"bytes,2,opt,name=datastore_name,json=datastoreName,proto3" json:"datastore_name,omitempty"` - Resource *ResourceSpecification `protobuf:"bytes,3,opt,name=resource,proto3" json:"resource,omitempty"` - NamespaceName string `protobuf:"bytes,4,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` -} - -func (x *CreateResourceRequest) Reset() { - *x = CreateResourceRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CreateResourceRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateResourceRequest) ProtoMessage() {} - -func (x *CreateResourceRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CreateResourceRequest.ProtoReflect.Descriptor instead. -func (*CreateResourceRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{4} -} - -func (x *CreateResourceRequest) GetProjectName() string { - if x != nil { - return x.ProjectName - } - return "" -} - -func (x *CreateResourceRequest) GetDatastoreName() string { - if x != nil { - return x.DatastoreName - } - return "" -} - -func (x *CreateResourceRequest) GetResource() *ResourceSpecification { - if x != nil { - return x.Resource - } - return nil -} - -func (x *CreateResourceRequest) GetNamespaceName() string { - if x != nil { - return x.NamespaceName - } - return "" -} - -type CreateResourceResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` - Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` -} - -func (x *CreateResourceResponse) Reset() { - *x = CreateResourceResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CreateResourceResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateResourceResponse) ProtoMessage() {} - -func (x *CreateResourceResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CreateResourceResponse.ProtoReflect.Descriptor instead. -func (*CreateResourceResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{5} -} - -func (x *CreateResourceResponse) GetSuccess() bool { - if x != nil { - return x.Success - } - return false -} - -func (x *CreateResourceResponse) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -type ReadResourceRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` - DatastoreName string `protobuf:"bytes,2,opt,name=datastore_name,json=datastoreName,proto3" json:"datastore_name,omitempty"` - ResourceName string `protobuf:"bytes,3,opt,name=resource_name,json=resourceName,proto3" json:"resource_name,omitempty"` - NamespaceName string `protobuf:"bytes,4,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` -} - -func (x *ReadResourceRequest) Reset() { - *x = ReadResourceRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ReadResourceRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReadResourceRequest) ProtoMessage() {} - -func (x *ReadResourceRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ReadResourceRequest.ProtoReflect.Descriptor instead. -func (*ReadResourceRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{6} -} - -func (x *ReadResourceRequest) GetProjectName() string { - if x != nil { - return x.ProjectName - } - return "" -} - -func (x *ReadResourceRequest) GetDatastoreName() string { - if x != nil { - return x.DatastoreName - } - return "" -} - -func (x *ReadResourceRequest) GetResourceName() string { - if x != nil { - return x.ResourceName - } - return "" -} - -func (x *ReadResourceRequest) GetNamespaceName() string { - if x != nil { - return x.NamespaceName - } - return "" -} - -type ReadResourceResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` - Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` - Resource *ResourceSpecification `protobuf:"bytes,3,opt,name=resource,proto3" json:"resource,omitempty"` -} - -func (x *ReadResourceResponse) Reset() { - *x = ReadResourceResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ReadResourceResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReadResourceResponse) ProtoMessage() {} - -func (x *ReadResourceResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ReadResourceResponse.ProtoReflect.Descriptor instead. -func (*ReadResourceResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{7} -} - -func (x *ReadResourceResponse) GetSuccess() bool { - if x != nil { - return x.Success - } - return false -} - -func (x *ReadResourceResponse) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -func (x *ReadResourceResponse) GetResource() *ResourceSpecification { - if x != nil { - return x.Resource - } - return nil -} - -type UpdateResourceRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` - DatastoreName string `protobuf:"bytes,2,opt,name=datastore_name,json=datastoreName,proto3" json:"datastore_name,omitempty"` - Resource *ResourceSpecification `protobuf:"bytes,3,opt,name=resource,proto3" json:"resource,omitempty"` - NamespaceName string `protobuf:"bytes,4,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` -} - -func (x *UpdateResourceRequest) Reset() { - *x = UpdateResourceRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *UpdateResourceRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UpdateResourceRequest) ProtoMessage() {} - -func (x *UpdateResourceRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UpdateResourceRequest.ProtoReflect.Descriptor instead. -func (*UpdateResourceRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{8} -} - -func (x *UpdateResourceRequest) GetProjectName() string { - if x != nil { - return x.ProjectName - } - return "" -} - -func (x *UpdateResourceRequest) GetDatastoreName() string { - if x != nil { - return x.DatastoreName - } - return "" -} - -func (x *UpdateResourceRequest) GetResource() *ResourceSpecification { - if x != nil { - return x.Resource - } - return nil -} - -func (x *UpdateResourceRequest) GetNamespaceName() string { - if x != nil { - return x.NamespaceName - } - return "" -} - -type UpdateResourceResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` - Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` -} - -func (x *UpdateResourceResponse) Reset() { - *x = UpdateResourceResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *UpdateResourceResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UpdateResourceResponse) ProtoMessage() {} - -func (x *UpdateResourceResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UpdateResourceResponse.ProtoReflect.Descriptor instead. -func (*UpdateResourceResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{9} -} - -func (x *UpdateResourceResponse) GetSuccess() bool { - if x != nil { - return x.Success - } - return false -} - -func (x *UpdateResourceResponse) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -// ResourceSpecification are datastore specification representation of a resource -type ResourceSpecification struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Version int32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - Type string `protobuf:"bytes,4,opt,name=type,proto3" json:"type,omitempty"` - Spec *structpb.Struct `protobuf:"bytes,5,opt,name=spec,proto3" json:"spec,omitempty"` - Assets map[string]string `protobuf:"bytes,6,rep,name=assets,proto3" json:"assets,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - Labels map[string]string `protobuf:"bytes,7,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` -} - -func (x *ResourceSpecification) Reset() { - *x = ResourceSpecification{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ResourceSpecification) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ResourceSpecification) ProtoMessage() {} - -func (x *ResourceSpecification) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[10] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ResourceSpecification.ProtoReflect.Descriptor instead. -func (*ResourceSpecification) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{10} -} - -func (x *ResourceSpecification) GetVersion() int32 { - if x != nil { - return x.Version - } - return 0 -} - -func (x *ResourceSpecification) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *ResourceSpecification) GetType() string { - if x != nil { - return x.Type - } - return "" -} - -func (x *ResourceSpecification) GetSpec() *structpb.Struct { - if x != nil { - return x.Spec - } - return nil -} - -func (x *ResourceSpecification) GetAssets() map[string]string { - if x != nil { - return x.Assets - } - return nil -} - -func (x *ResourceSpecification) GetLabels() map[string]string { - if x != nil { - return x.Labels - } - return nil -} - -var File_odpf_optimus_core_v1beta1_resource_proto protoreflect.FileDescriptor - -var file_odpf_optimus_core_v1beta1_resource_proto_rawDesc = []byte{ - 0x0a, 0x28, 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, - 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x6f, 0x64, 0x70, 0x66, - 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, - 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x1a, 0x26, 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, - 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, - 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe5, 0x01, 0x0a, 0x22, 0x44, 0x65, - 0x70, 0x6c, 0x6f, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x61, 0x74, - 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x4e, 0x0a, 0x09, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, - 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, - 0x65, 0x22, 0x6a, 0x0a, 0x23, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x5f, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6f, - 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x09, 0x6c, 0x6f, - 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x05, 0x22, 0x93, 0x01, - 0x0a, 0x20, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, - 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, - 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64, - 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x22, 0x73, 0x0a, 0x21, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x6f, 0x64, - 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x22, 0xd6, 0x01, 0x0a, 0x15, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, - 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64, - 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x4c, 0x0a, 0x08, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, - 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, - 0x65, 0x22, 0x4c, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, - 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, - 0xab, 0x01, 0x0a, 0x13, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x61, - 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x98, 0x01, - 0x0a, 0x14, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x4c, 0x0a, 0x08, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x6f, - 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0xd6, 0x01, 0x0a, 0x15, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, - 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64, - 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x4c, 0x0a, 0x08, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, - 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, - 0x65, 0x22, 0x4c, 0x0a, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, - 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, - 0xae, 0x03, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x73, - 0x70, 0x65, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, - 0x63, 0x74, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x54, 0x0a, 0x06, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, - 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, - 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x54, - 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3c, - 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, - 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, - 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, - 0x32, 0xe7, 0x08, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x12, 0xa2, 0x01, 0x0a, 0x1b, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, - 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, - 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x3e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, - 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, - 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0xfe, 0x01, 0x0a, 0x19, 0x4c, 0x69, - 0x73, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, - 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3c, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, - 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, - 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x66, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x60, 0x12, 0x5e, 0x2f, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x2f, 0x7b, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x7d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0xe0, 0x01, 0x0a, 0x0e, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x30, 0x2e, - 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x31, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x69, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x63, 0x22, 0x5e, 0x2f, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x2f, 0x7b, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x7d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0xe7, 0x01, - 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x2e, - 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, - 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x76, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x70, 0x12, 0x6e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x7d, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x7b, 0x64, - 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2f, 0x7b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0xe0, 0x01, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x30, 0x2e, 0x6f, 0x64, 0x70, - 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x6f, - 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x69, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x63, 0x1a, 0x5e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x7d, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x7b, 0x64, - 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x3a, 0x01, 0x2a, 0x42, 0x9c, 0x01, 0x0a, 0x16, 0x69, - 0x6f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2e, 0x6f, 0x70, - 0x74, 0x69, 0x6d, 0x75, 0x73, 0x42, 0x16, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x50, 0x01, 0x5a, - 0x1e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x64, 0x70, 0x66, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x92, - 0x41, 0x47, 0x12, 0x05, 0x32, 0x03, 0x30, 0x2e, 0x31, 0x1a, 0x0e, 0x31, 0x32, 0x37, 0x2e, 0x30, - 0x2e, 0x30, 0x2e, 0x31, 0x3a, 0x39, 0x31, 0x30, 0x30, 0x22, 0x04, 0x2f, 0x61, 0x70, 0x69, 0x2a, - 0x01, 0x01, 0x72, 0x25, 0x0a, 0x23, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x20, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, -} - -var ( - file_odpf_optimus_core_v1beta1_resource_proto_rawDescOnce sync.Once - file_odpf_optimus_core_v1beta1_resource_proto_rawDescData = file_odpf_optimus_core_v1beta1_resource_proto_rawDesc -) - -func file_odpf_optimus_core_v1beta1_resource_proto_rawDescGZIP() []byte { - file_odpf_optimus_core_v1beta1_resource_proto_rawDescOnce.Do(func() { - file_odpf_optimus_core_v1beta1_resource_proto_rawDescData = protoimpl.X.CompressGZIP(file_odpf_optimus_core_v1beta1_resource_proto_rawDescData) - }) - return file_odpf_optimus_core_v1beta1_resource_proto_rawDescData -} - -var file_odpf_optimus_core_v1beta1_resource_proto_msgTypes = make([]protoimpl.MessageInfo, 13) -var file_odpf_optimus_core_v1beta1_resource_proto_goTypes = []interface{}{ - (*DeployResourceSpecificationRequest)(nil), // 0: odpf.optimus.core.v1beta1.DeployResourceSpecificationRequest - (*DeployResourceSpecificationResponse)(nil), // 1: odpf.optimus.core.v1beta1.DeployResourceSpecificationResponse - (*ListResourceSpecificationRequest)(nil), // 2: odpf.optimus.core.v1beta1.ListResourceSpecificationRequest - (*ListResourceSpecificationResponse)(nil), // 3: odpf.optimus.core.v1beta1.ListResourceSpecificationResponse - (*CreateResourceRequest)(nil), // 4: odpf.optimus.core.v1beta1.CreateResourceRequest - (*CreateResourceResponse)(nil), // 5: odpf.optimus.core.v1beta1.CreateResourceResponse - (*ReadResourceRequest)(nil), // 6: odpf.optimus.core.v1beta1.ReadResourceRequest - (*ReadResourceResponse)(nil), // 7: odpf.optimus.core.v1beta1.ReadResourceResponse - (*UpdateResourceRequest)(nil), // 8: odpf.optimus.core.v1beta1.UpdateResourceRequest - (*UpdateResourceResponse)(nil), // 9: odpf.optimus.core.v1beta1.UpdateResourceResponse - (*ResourceSpecification)(nil), // 10: odpf.optimus.core.v1beta1.ResourceSpecification - nil, // 11: odpf.optimus.core.v1beta1.ResourceSpecification.AssetsEntry - nil, // 12: odpf.optimus.core.v1beta1.ResourceSpecification.LabelsEntry - (*Log)(nil), // 13: odpf.optimus.core.v1beta1.Log - (*structpb.Struct)(nil), // 14: google.protobuf.Struct -} -var file_odpf_optimus_core_v1beta1_resource_proto_depIdxs = []int32{ - 10, // 0: odpf.optimus.core.v1beta1.DeployResourceSpecificationRequest.resources:type_name -> odpf.optimus.core.v1beta1.ResourceSpecification - 13, // 1: odpf.optimus.core.v1beta1.DeployResourceSpecificationResponse.log_status:type_name -> odpf.optimus.core.v1beta1.Log - 10, // 2: odpf.optimus.core.v1beta1.ListResourceSpecificationResponse.resources:type_name -> odpf.optimus.core.v1beta1.ResourceSpecification - 10, // 3: odpf.optimus.core.v1beta1.CreateResourceRequest.resource:type_name -> odpf.optimus.core.v1beta1.ResourceSpecification - 10, // 4: odpf.optimus.core.v1beta1.ReadResourceResponse.resource:type_name -> odpf.optimus.core.v1beta1.ResourceSpecification - 10, // 5: odpf.optimus.core.v1beta1.UpdateResourceRequest.resource:type_name -> odpf.optimus.core.v1beta1.ResourceSpecification - 14, // 6: odpf.optimus.core.v1beta1.ResourceSpecification.spec:type_name -> google.protobuf.Struct - 11, // 7: odpf.optimus.core.v1beta1.ResourceSpecification.assets:type_name -> odpf.optimus.core.v1beta1.ResourceSpecification.AssetsEntry - 12, // 8: odpf.optimus.core.v1beta1.ResourceSpecification.labels:type_name -> odpf.optimus.core.v1beta1.ResourceSpecification.LabelsEntry - 0, // 9: odpf.optimus.core.v1beta1.ResourceService.DeployResourceSpecification:input_type -> odpf.optimus.core.v1beta1.DeployResourceSpecificationRequest - 2, // 10: odpf.optimus.core.v1beta1.ResourceService.ListResourceSpecification:input_type -> odpf.optimus.core.v1beta1.ListResourceSpecificationRequest - 4, // 11: odpf.optimus.core.v1beta1.ResourceService.CreateResource:input_type -> odpf.optimus.core.v1beta1.CreateResourceRequest - 6, // 12: odpf.optimus.core.v1beta1.ResourceService.ReadResource:input_type -> odpf.optimus.core.v1beta1.ReadResourceRequest - 8, // 13: odpf.optimus.core.v1beta1.ResourceService.UpdateResource:input_type -> odpf.optimus.core.v1beta1.UpdateResourceRequest - 1, // 14: odpf.optimus.core.v1beta1.ResourceService.DeployResourceSpecification:output_type -> odpf.optimus.core.v1beta1.DeployResourceSpecificationResponse - 3, // 15: odpf.optimus.core.v1beta1.ResourceService.ListResourceSpecification:output_type -> odpf.optimus.core.v1beta1.ListResourceSpecificationResponse - 5, // 16: odpf.optimus.core.v1beta1.ResourceService.CreateResource:output_type -> odpf.optimus.core.v1beta1.CreateResourceResponse - 7, // 17: odpf.optimus.core.v1beta1.ResourceService.ReadResource:output_type -> odpf.optimus.core.v1beta1.ReadResourceResponse - 9, // 18: odpf.optimus.core.v1beta1.ResourceService.UpdateResource:output_type -> odpf.optimus.core.v1beta1.UpdateResourceResponse - 14, // [14:19] is the sub-list for method output_type - 9, // [9:14] is the sub-list for method input_type - 9, // [9:9] is the sub-list for extension type_name - 9, // [9:9] is the sub-list for extension extendee - 0, // [0:9] is the sub-list for field type_name -} - -func init() { file_odpf_optimus_core_v1beta1_resource_proto_init() } -func file_odpf_optimus_core_v1beta1_resource_proto_init() { - if File_odpf_optimus_core_v1beta1_resource_proto != nil { - return - } - file_odpf_optimus_core_v1beta1_status_proto_init() - if !protoimpl.UnsafeEnabled { - file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeployResourceSpecificationRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeployResourceSpecificationResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListResourceSpecificationRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListResourceSpecificationResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateResourceRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateResourceResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReadResourceRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReadResourceResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateResourceRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateResourceResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_resource_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ResourceSpecification); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_odpf_optimus_core_v1beta1_resource_proto_rawDesc, - NumEnums: 0, - NumMessages: 13, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_odpf_optimus_core_v1beta1_resource_proto_goTypes, - DependencyIndexes: file_odpf_optimus_core_v1beta1_resource_proto_depIdxs, - MessageInfos: file_odpf_optimus_core_v1beta1_resource_proto_msgTypes, - }.Build() - File_odpf_optimus_core_v1beta1_resource_proto = out.File - file_odpf_optimus_core_v1beta1_resource_proto_rawDesc = nil - file_odpf_optimus_core_v1beta1_resource_proto_goTypes = nil - file_odpf_optimus_core_v1beta1_resource_proto_depIdxs = nil -} diff --git a/protos/odpf/optimus/core/v1beta1/runtime.pb.go b/protos/odpf/optimus/core/v1beta1/runtime.pb.go deleted file mode 100644 index 372c8f84d7..0000000000 --- a/protos/odpf/optimus/core/v1beta1/runtime.pb.go +++ /dev/null @@ -1,234 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.0 -// protoc (unknown) -// source: odpf/optimus/core/v1beta1/runtime.proto - -package optimus - -import ( - _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" - _ "google.golang.org/genproto/googleapis/api/annotations" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type VersionRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Client string `protobuf:"bytes,1,opt,name=client,proto3" json:"client,omitempty"` -} - -func (x *VersionRequest) Reset() { - *x = VersionRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_runtime_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *VersionRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*VersionRequest) ProtoMessage() {} - -func (x *VersionRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_runtime_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use VersionRequest.ProtoReflect.Descriptor instead. -func (*VersionRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_runtime_proto_rawDescGZIP(), []int{0} -} - -func (x *VersionRequest) GetClient() string { - if x != nil { - return x.Client - } - return "" -} - -type VersionResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Server string `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"` -} - -func (x *VersionResponse) Reset() { - *x = VersionResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_runtime_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *VersionResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*VersionResponse) ProtoMessage() {} - -func (x *VersionResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_runtime_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use VersionResponse.ProtoReflect.Descriptor instead. -func (*VersionResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_runtime_proto_rawDescGZIP(), []int{1} -} - -func (x *VersionResponse) GetServer() string { - if x != nil { - return x.Server - } - return "" -} - -var File_odpf_optimus_core_v1beta1_runtime_proto protoreflect.FileDescriptor - -var file_odpf_optimus_core_v1beta1_runtime_proto_rawDesc = []byte{ - 0x0a, 0x27, 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, - 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x72, 0x75, 0x6e, 0x74, - 0x69, 0x6d, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x6f, 0x64, 0x70, 0x66, 0x2e, - 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, - 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, - 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0x28, 0x0a, 0x0e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x22, 0x29, 0x0a, 0x0f, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x32, 0x8f, 0x01, 0x0a, 0x0e, 0x52, 0x75, 0x6e, 0x74, - 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x7d, 0x0a, 0x07, 0x56, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, - 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x2a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x56, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x15, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x01, 0x2a, 0x42, 0x8f, 0x01, 0x0a, 0x16, 0x69, 0x6f, - 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2e, 0x6f, 0x70, 0x74, - 0x69, 0x6d, 0x75, 0x73, 0x42, 0x15, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x1e, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x92, 0x41, 0x3b, - 0x12, 0x05, 0x32, 0x03, 0x30, 0x2e, 0x31, 0x1a, 0x0e, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, - 0x2e, 0x31, 0x3a, 0x39, 0x31, 0x30, 0x30, 0x22, 0x04, 0x2f, 0x61, 0x70, 0x69, 0x2a, 0x01, 0x01, - 0x72, 0x19, 0x0a, 0x17, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x20, 0x52, 0x75, 0x6e, 0x74, - 0x69, 0x6d, 0x65, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, -} - -var ( - file_odpf_optimus_core_v1beta1_runtime_proto_rawDescOnce sync.Once - file_odpf_optimus_core_v1beta1_runtime_proto_rawDescData = file_odpf_optimus_core_v1beta1_runtime_proto_rawDesc -) - -func file_odpf_optimus_core_v1beta1_runtime_proto_rawDescGZIP() []byte { - file_odpf_optimus_core_v1beta1_runtime_proto_rawDescOnce.Do(func() { - file_odpf_optimus_core_v1beta1_runtime_proto_rawDescData = protoimpl.X.CompressGZIP(file_odpf_optimus_core_v1beta1_runtime_proto_rawDescData) - }) - return file_odpf_optimus_core_v1beta1_runtime_proto_rawDescData -} - -var file_odpf_optimus_core_v1beta1_runtime_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_odpf_optimus_core_v1beta1_runtime_proto_goTypes = []interface{}{ - (*VersionRequest)(nil), // 0: odpf.optimus.core.v1beta1.VersionRequest - (*VersionResponse)(nil), // 1: odpf.optimus.core.v1beta1.VersionResponse -} -var file_odpf_optimus_core_v1beta1_runtime_proto_depIdxs = []int32{ - 0, // 0: odpf.optimus.core.v1beta1.RuntimeService.Version:input_type -> odpf.optimus.core.v1beta1.VersionRequest - 1, // 1: odpf.optimus.core.v1beta1.RuntimeService.Version:output_type -> odpf.optimus.core.v1beta1.VersionResponse - 1, // [1:2] is the sub-list for method output_type - 0, // [0:1] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name -} - -func init() { file_odpf_optimus_core_v1beta1_runtime_proto_init() } -func file_odpf_optimus_core_v1beta1_runtime_proto_init() { - if File_odpf_optimus_core_v1beta1_runtime_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_odpf_optimus_core_v1beta1_runtime_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VersionRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_runtime_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VersionResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_odpf_optimus_core_v1beta1_runtime_proto_rawDesc, - NumEnums: 0, - NumMessages: 2, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_odpf_optimus_core_v1beta1_runtime_proto_goTypes, - DependencyIndexes: file_odpf_optimus_core_v1beta1_runtime_proto_depIdxs, - MessageInfos: file_odpf_optimus_core_v1beta1_runtime_proto_msgTypes, - }.Build() - File_odpf_optimus_core_v1beta1_runtime_proto = out.File - file_odpf_optimus_core_v1beta1_runtime_proto_rawDesc = nil - file_odpf_optimus_core_v1beta1_runtime_proto_goTypes = nil - file_odpf_optimus_core_v1beta1_runtime_proto_depIdxs = nil -} diff --git a/protos/odpf/optimus/core/v1beta1/secret.pb.go b/protos/odpf/optimus/core/v1beta1/secret.pb.go deleted file mode 100644 index f14639344c..0000000000 --- a/protos/odpf/optimus/core/v1beta1/secret.pb.go +++ /dev/null @@ -1,809 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.0 -// protoc (unknown) -// source: odpf/optimus/core/v1beta1/secret.proto - -package optimus - -import ( - _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" - _ "google.golang.org/genproto/googleapis/api/annotations" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - timestamppb "google.golang.org/protobuf/types/known/timestamppb" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type RegisterSecretRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` - SecretName string `protobuf:"bytes,2,opt,name=secret_name,json=secretName,proto3" json:"secret_name,omitempty"` - Value string `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` // base64 encoded secret value - NamespaceName string `protobuf:"bytes,4,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` -} - -func (x *RegisterSecretRequest) Reset() { - *x = RegisterSecretRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RegisterSecretRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RegisterSecretRequest) ProtoMessage() {} - -func (x *RegisterSecretRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RegisterSecretRequest.ProtoReflect.Descriptor instead. -func (*RegisterSecretRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_secret_proto_rawDescGZIP(), []int{0} -} - -func (x *RegisterSecretRequest) GetProjectName() string { - if x != nil { - return x.ProjectName - } - return "" -} - -func (x *RegisterSecretRequest) GetSecretName() string { - if x != nil { - return x.SecretName - } - return "" -} - -func (x *RegisterSecretRequest) GetValue() string { - if x != nil { - return x.Value - } - return "" -} - -func (x *RegisterSecretRequest) GetNamespaceName() string { - if x != nil { - return x.NamespaceName - } - return "" -} - -type RegisterSecretResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *RegisterSecretResponse) Reset() { - *x = RegisterSecretResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RegisterSecretResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RegisterSecretResponse) ProtoMessage() {} - -func (x *RegisterSecretResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RegisterSecretResponse.ProtoReflect.Descriptor instead. -func (*RegisterSecretResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_secret_proto_rawDescGZIP(), []int{1} -} - -type UpdateSecretRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` - SecretName string `protobuf:"bytes,2,opt,name=secret_name,json=secretName,proto3" json:"secret_name,omitempty"` - Value string `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` // base64 encoded secret value - NamespaceName string `protobuf:"bytes,4,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` -} - -func (x *UpdateSecretRequest) Reset() { - *x = UpdateSecretRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *UpdateSecretRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UpdateSecretRequest) ProtoMessage() {} - -func (x *UpdateSecretRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UpdateSecretRequest.ProtoReflect.Descriptor instead. -func (*UpdateSecretRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_secret_proto_rawDescGZIP(), []int{2} -} - -func (x *UpdateSecretRequest) GetProjectName() string { - if x != nil { - return x.ProjectName - } - return "" -} - -func (x *UpdateSecretRequest) GetSecretName() string { - if x != nil { - return x.SecretName - } - return "" -} - -func (x *UpdateSecretRequest) GetValue() string { - if x != nil { - return x.Value - } - return "" -} - -func (x *UpdateSecretRequest) GetNamespaceName() string { - if x != nil { - return x.NamespaceName - } - return "" -} - -type UpdateSecretResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *UpdateSecretResponse) Reset() { - *x = UpdateSecretResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *UpdateSecretResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UpdateSecretResponse) ProtoMessage() {} - -func (x *UpdateSecretResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UpdateSecretResponse.ProtoReflect.Descriptor instead. -func (*UpdateSecretResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_secret_proto_rawDescGZIP(), []int{3} -} - -type ListSecretsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` -} - -func (x *ListSecretsRequest) Reset() { - *x = ListSecretsRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ListSecretsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListSecretsRequest) ProtoMessage() {} - -func (x *ListSecretsRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListSecretsRequest.ProtoReflect.Descriptor instead. -func (*ListSecretsRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_secret_proto_rawDescGZIP(), []int{4} -} - -func (x *ListSecretsRequest) GetProjectName() string { - if x != nil { - return x.ProjectName - } - return "" -} - -type ListSecretsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Secrets []*ListSecretsResponse_Secret `protobuf:"bytes,1,rep,name=secrets,proto3" json:"secrets,omitempty"` -} - -func (x *ListSecretsResponse) Reset() { - *x = ListSecretsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ListSecretsResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListSecretsResponse) ProtoMessage() {} - -func (x *ListSecretsResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListSecretsResponse.ProtoReflect.Descriptor instead. -func (*ListSecretsResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_secret_proto_rawDescGZIP(), []int{5} -} - -func (x *ListSecretsResponse) GetSecrets() []*ListSecretsResponse_Secret { - if x != nil { - return x.Secrets - } - return nil -} - -type DeleteSecretRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` - SecretName string `protobuf:"bytes,2,opt,name=secret_name,json=secretName,proto3" json:"secret_name,omitempty"` - NamespaceName string `protobuf:"bytes,3,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` -} - -func (x *DeleteSecretRequest) Reset() { - *x = DeleteSecretRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DeleteSecretRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteSecretRequest) ProtoMessage() {} - -func (x *DeleteSecretRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteSecretRequest.ProtoReflect.Descriptor instead. -func (*DeleteSecretRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_secret_proto_rawDescGZIP(), []int{6} -} - -func (x *DeleteSecretRequest) GetProjectName() string { - if x != nil { - return x.ProjectName - } - return "" -} - -func (x *DeleteSecretRequest) GetSecretName() string { - if x != nil { - return x.SecretName - } - return "" -} - -func (x *DeleteSecretRequest) GetNamespaceName() string { - if x != nil { - return x.NamespaceName - } - return "" -} - -type DeleteSecretResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *DeleteSecretResponse) Reset() { - *x = DeleteSecretResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DeleteSecretResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteSecretResponse) ProtoMessage() {} - -func (x *DeleteSecretResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteSecretResponse.ProtoReflect.Descriptor instead. -func (*DeleteSecretResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_secret_proto_rawDescGZIP(), []int{7} -} - -type ListSecretsResponse_Secret struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Digest string `protobuf:"bytes,2,opt,name=digest,proto3" json:"digest,omitempty"` - Namespace string `protobuf:"bytes,3,opt,name=namespace,proto3" json:"namespace,omitempty"` - UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` -} - -func (x *ListSecretsResponse_Secret) Reset() { - *x = ListSecretsResponse_Secret{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ListSecretsResponse_Secret) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListSecretsResponse_Secret) ProtoMessage() {} - -func (x *ListSecretsResponse_Secret) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListSecretsResponse_Secret.ProtoReflect.Descriptor instead. -func (*ListSecretsResponse_Secret) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_secret_proto_rawDescGZIP(), []int{5, 0} -} - -func (x *ListSecretsResponse_Secret) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *ListSecretsResponse_Secret) GetDigest() string { - if x != nil { - return x.Digest - } - return "" -} - -func (x *ListSecretsResponse_Secret) GetNamespace() string { - if x != nil { - return x.Namespace - } - return "" -} - -func (x *ListSecretsResponse_Secret) GetUpdatedAt() *timestamppb.Timestamp { - if x != nil { - return x.UpdatedAt - } - return nil -} - -var File_odpf_optimus_core_v1beta1_secret_proto protoreflect.FileDescriptor - -var file_odpf_optimus_core_v1beta1_secret_proto_rawDesc = []byte{ - 0x0a, 0x26, 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, - 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, 0x65, 0x63, 0x72, - 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, - 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, - 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, - 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0x98, 0x01, 0x0a, 0x15, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x53, - 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x18, 0x0a, - 0x16, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x96, 0x01, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, - 0x22, 0x16, 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x37, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, - 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, - 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, - 0x65, 0x22, 0xf6, 0x01, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x07, 0x73, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x6f, 0x64, 0x70, - 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x52, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x1a, 0x8d, 0x01, 0x0a, 0x06, 0x53, - 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x67, - 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, - 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, - 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x80, 0x01, 0x0a, 0x13, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x63, 0x72, - 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x16, 0x0a, - 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xca, 0x05, 0x0a, 0x0d, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xb6, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x65, 0x72, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x30, 0x2e, 0x6f, 0x64, 0x70, - 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x53, - 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x6f, - 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, - 0x72, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x3f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x39, 0x22, 0x34, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x2f, - 0x7b, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x3a, 0x01, 0x2a, - 0x12, 0xb0, 0x01, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x12, 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, - 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, - 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x3f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x39, 0x1a, 0x34, 0x2f, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x73, 0x65, 0x63, 0x72, - 0x65, 0x74, 0x2f, 0x7b, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, - 0x3a, 0x01, 0x2a, 0x12, 0x9c, 0x01, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x63, 0x72, - 0x65, 0x74, 0x73, 0x12, 0x2d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, - 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, - 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x12, 0x26, 0x2f, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x73, 0x65, 0x63, 0x72, - 0x65, 0x74, 0x12, 0xad, 0x01, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x12, 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, - 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, - 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x36, 0x2a, 0x34, 0x2f, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x73, 0x65, - 0x63, 0x72, 0x65, 0x74, 0x2f, 0x7b, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x7d, 0x42, 0x98, 0x01, 0x0a, 0x16, 0x69, 0x6f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x42, 0x14, 0x53, - 0x65, 0x63, 0x72, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x61, 0x6e, 0x61, - 0x67, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x1e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, 0x6f, 0x70, - 0x74, 0x69, 0x6d, 0x75, 0x73, 0x92, 0x41, 0x45, 0x12, 0x05, 0x32, 0x03, 0x30, 0x2e, 0x31, 0x1a, - 0x0e, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x3a, 0x39, 0x31, 0x30, 0x30, 0x22, - 0x04, 0x2f, 0x61, 0x70, 0x69, 0x2a, 0x01, 0x01, 0x72, 0x23, 0x0a, 0x21, 0x4f, 0x70, 0x74, 0x69, - 0x6d, 0x75, 0x73, 0x20, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x20, 0x4d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_odpf_optimus_core_v1beta1_secret_proto_rawDescOnce sync.Once - file_odpf_optimus_core_v1beta1_secret_proto_rawDescData = file_odpf_optimus_core_v1beta1_secret_proto_rawDesc -) - -func file_odpf_optimus_core_v1beta1_secret_proto_rawDescGZIP() []byte { - file_odpf_optimus_core_v1beta1_secret_proto_rawDescOnce.Do(func() { - file_odpf_optimus_core_v1beta1_secret_proto_rawDescData = protoimpl.X.CompressGZIP(file_odpf_optimus_core_v1beta1_secret_proto_rawDescData) - }) - return file_odpf_optimus_core_v1beta1_secret_proto_rawDescData -} - -var file_odpf_optimus_core_v1beta1_secret_proto_msgTypes = make([]protoimpl.MessageInfo, 9) -var file_odpf_optimus_core_v1beta1_secret_proto_goTypes = []interface{}{ - (*RegisterSecretRequest)(nil), // 0: odpf.optimus.core.v1beta1.RegisterSecretRequest - (*RegisterSecretResponse)(nil), // 1: odpf.optimus.core.v1beta1.RegisterSecretResponse - (*UpdateSecretRequest)(nil), // 2: odpf.optimus.core.v1beta1.UpdateSecretRequest - (*UpdateSecretResponse)(nil), // 3: odpf.optimus.core.v1beta1.UpdateSecretResponse - (*ListSecretsRequest)(nil), // 4: odpf.optimus.core.v1beta1.ListSecretsRequest - (*ListSecretsResponse)(nil), // 5: odpf.optimus.core.v1beta1.ListSecretsResponse - (*DeleteSecretRequest)(nil), // 6: odpf.optimus.core.v1beta1.DeleteSecretRequest - (*DeleteSecretResponse)(nil), // 7: odpf.optimus.core.v1beta1.DeleteSecretResponse - (*ListSecretsResponse_Secret)(nil), // 8: odpf.optimus.core.v1beta1.ListSecretsResponse.Secret - (*timestamppb.Timestamp)(nil), // 9: google.protobuf.Timestamp -} -var file_odpf_optimus_core_v1beta1_secret_proto_depIdxs = []int32{ - 8, // 0: odpf.optimus.core.v1beta1.ListSecretsResponse.secrets:type_name -> odpf.optimus.core.v1beta1.ListSecretsResponse.Secret - 9, // 1: odpf.optimus.core.v1beta1.ListSecretsResponse.Secret.updated_at:type_name -> google.protobuf.Timestamp - 0, // 2: odpf.optimus.core.v1beta1.SecretService.RegisterSecret:input_type -> odpf.optimus.core.v1beta1.RegisterSecretRequest - 2, // 3: odpf.optimus.core.v1beta1.SecretService.UpdateSecret:input_type -> odpf.optimus.core.v1beta1.UpdateSecretRequest - 4, // 4: odpf.optimus.core.v1beta1.SecretService.ListSecrets:input_type -> odpf.optimus.core.v1beta1.ListSecretsRequest - 6, // 5: odpf.optimus.core.v1beta1.SecretService.DeleteSecret:input_type -> odpf.optimus.core.v1beta1.DeleteSecretRequest - 1, // 6: odpf.optimus.core.v1beta1.SecretService.RegisterSecret:output_type -> odpf.optimus.core.v1beta1.RegisterSecretResponse - 3, // 7: odpf.optimus.core.v1beta1.SecretService.UpdateSecret:output_type -> odpf.optimus.core.v1beta1.UpdateSecretResponse - 5, // 8: odpf.optimus.core.v1beta1.SecretService.ListSecrets:output_type -> odpf.optimus.core.v1beta1.ListSecretsResponse - 7, // 9: odpf.optimus.core.v1beta1.SecretService.DeleteSecret:output_type -> odpf.optimus.core.v1beta1.DeleteSecretResponse - 6, // [6:10] is the sub-list for method output_type - 2, // [2:6] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name -} - -func init() { file_odpf_optimus_core_v1beta1_secret_proto_init() } -func file_odpf_optimus_core_v1beta1_secret_proto_init() { - if File_odpf_optimus_core_v1beta1_secret_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RegisterSecretRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RegisterSecretResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateSecretRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateSecretResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListSecretsRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListSecretsResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteSecretRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteSecretResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_core_v1beta1_secret_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListSecretsResponse_Secret); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_odpf_optimus_core_v1beta1_secret_proto_rawDesc, - NumEnums: 0, - NumMessages: 9, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_odpf_optimus_core_v1beta1_secret_proto_goTypes, - DependencyIndexes: file_odpf_optimus_core_v1beta1_secret_proto_depIdxs, - MessageInfos: file_odpf_optimus_core_v1beta1_secret_proto_msgTypes, - }.Build() - File_odpf_optimus_core_v1beta1_secret_proto = out.File - file_odpf_optimus_core_v1beta1_secret_proto_rawDesc = nil - file_odpf_optimus_core_v1beta1_secret_proto_goTypes = nil - file_odpf_optimus_core_v1beta1_secret_proto_depIdxs = nil -} diff --git a/protos/odpf/optimus/core/v1beta1/status.pb.go b/protos/odpf/optimus/core/v1beta1/status.pb.go deleted file mode 100644 index 2fe065f402..0000000000 --- a/protos/odpf/optimus/core/v1beta1/status.pb.go +++ /dev/null @@ -1,232 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.0 -// protoc (unknown) -// source: odpf/optimus/core/v1beta1/status.proto - -package optimus - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type Level int32 - -const ( - Level_LEVEL_UNSPECIFIED Level = 0 - Level_LEVEL_TRACE Level = 1 - Level_LEVEL_DEBUG Level = 2 - Level_LEVEL_INFO Level = 3 - Level_LEVEL_WARNING Level = 4 - Level_LEVEL_ERROR Level = 5 - Level_LEVEL_FATAL Level = 6 -) - -// Enum value maps for Level. -var ( - Level_name = map[int32]string{ - 0: "LEVEL_UNSPECIFIED", - 1: "LEVEL_TRACE", - 2: "LEVEL_DEBUG", - 3: "LEVEL_INFO", - 4: "LEVEL_WARNING", - 5: "LEVEL_ERROR", - 6: "LEVEL_FATAL", - } - Level_value = map[string]int32{ - "LEVEL_UNSPECIFIED": 0, - "LEVEL_TRACE": 1, - "LEVEL_DEBUG": 2, - "LEVEL_INFO": 3, - "LEVEL_WARNING": 4, - "LEVEL_ERROR": 5, - "LEVEL_FATAL": 6, - } -) - -func (x Level) Enum() *Level { - p := new(Level) - *p = x - return p -} - -func (x Level) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (Level) Descriptor() protoreflect.EnumDescriptor { - return file_odpf_optimus_core_v1beta1_status_proto_enumTypes[0].Descriptor() -} - -func (Level) Type() protoreflect.EnumType { - return &file_odpf_optimus_core_v1beta1_status_proto_enumTypes[0] -} - -func (x Level) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use Level.Descriptor instead. -func (Level) EnumDescriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_status_proto_rawDescGZIP(), []int{0} -} - -type Log struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Level Level `protobuf:"varint,1,opt,name=level,proto3,enum=odpf.optimus.core.v1beta1.Level" json:"level,omitempty"` - Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` -} - -func (x *Log) Reset() { - *x = Log{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_status_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Log) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Log) ProtoMessage() {} - -func (x *Log) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_status_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Log.ProtoReflect.Descriptor instead. -func (*Log) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_status_proto_rawDescGZIP(), []int{0} -} - -func (x *Log) GetLevel() Level { - if x != nil { - return x.Level - } - return Level_LEVEL_UNSPECIFIED -} - -func (x *Log) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -var File_odpf_optimus_core_v1beta1_status_proto protoreflect.FileDescriptor - -var file_odpf_optimus_core_v1beta1_status_proto_rawDesc = []byte{ - 0x0a, 0x26, 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, - 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, - 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x22, 0x57, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x36, 0x0a, 0x05, 0x6c, 0x65, - 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x6f, 0x64, 0x70, 0x66, - 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, - 0x65, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2a, 0x85, 0x01, 0x0a, - 0x05, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x15, 0x0a, 0x11, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, - 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0f, 0x0a, - 0x0b, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x01, 0x12, 0x0f, - 0x0a, 0x0b, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x02, 0x12, - 0x0e, 0x0a, 0x0a, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x03, 0x12, - 0x11, 0x0a, 0x0d, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x57, 0x41, 0x52, 0x4e, 0x49, 0x4e, 0x47, - 0x10, 0x04, 0x12, 0x0f, 0x0a, 0x0b, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x45, 0x52, 0x52, 0x4f, - 0x52, 0x10, 0x05, 0x12, 0x0f, 0x0a, 0x0b, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x46, 0x41, 0x54, - 0x41, 0x4c, 0x10, 0x06, 0x42, 0x42, 0x0a, 0x16, 0x69, 0x6f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x42, 0x06, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x50, 0x01, 0x5a, 0x1e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, - 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_odpf_optimus_core_v1beta1_status_proto_rawDescOnce sync.Once - file_odpf_optimus_core_v1beta1_status_proto_rawDescData = file_odpf_optimus_core_v1beta1_status_proto_rawDesc -) - -func file_odpf_optimus_core_v1beta1_status_proto_rawDescGZIP() []byte { - file_odpf_optimus_core_v1beta1_status_proto_rawDescOnce.Do(func() { - file_odpf_optimus_core_v1beta1_status_proto_rawDescData = protoimpl.X.CompressGZIP(file_odpf_optimus_core_v1beta1_status_proto_rawDescData) - }) - return file_odpf_optimus_core_v1beta1_status_proto_rawDescData -} - -var file_odpf_optimus_core_v1beta1_status_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_odpf_optimus_core_v1beta1_status_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_odpf_optimus_core_v1beta1_status_proto_goTypes = []interface{}{ - (Level)(0), // 0: odpf.optimus.core.v1beta1.Level - (*Log)(nil), // 1: odpf.optimus.core.v1beta1.Log -} -var file_odpf_optimus_core_v1beta1_status_proto_depIdxs = []int32{ - 0, // 0: odpf.optimus.core.v1beta1.Log.level:type_name -> odpf.optimus.core.v1beta1.Level - 1, // [1:1] is the sub-list for method output_type - 1, // [1:1] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name -} - -func init() { file_odpf_optimus_core_v1beta1_status_proto_init() } -func file_odpf_optimus_core_v1beta1_status_proto_init() { - if File_odpf_optimus_core_v1beta1_status_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_odpf_optimus_core_v1beta1_status_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Log); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_odpf_optimus_core_v1beta1_status_proto_rawDesc, - NumEnums: 1, - NumMessages: 1, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_odpf_optimus_core_v1beta1_status_proto_goTypes, - DependencyIndexes: file_odpf_optimus_core_v1beta1_status_proto_depIdxs, - EnumInfos: file_odpf_optimus_core_v1beta1_status_proto_enumTypes, - MessageInfos: file_odpf_optimus_core_v1beta1_status_proto_msgTypes, - }.Build() - File_odpf_optimus_core_v1beta1_status_proto = out.File - file_odpf_optimus_core_v1beta1_status_proto_rawDesc = nil - file_odpf_optimus_core_v1beta1_status_proto_goTypes = nil - file_odpf_optimus_core_v1beta1_status_proto_depIdxs = nil -} diff --git a/protos/odpf/optimus/plugins/v1beta1/dependency_resolver.pb.go b/protos/odpf/optimus/plugins/v1beta1/dependency_resolver.pb.go deleted file mode 100644 index 95f7a8f851..0000000000 --- a/protos/odpf/optimus/plugins/v1beta1/dependency_resolver.pb.go +++ /dev/null @@ -1,1182 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.0 -// protoc (unknown) -// source: odpf/optimus/plugins/v1beta1/dependency_resolver.proto - -package optimus - -import ( - v1beta1 "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - timestamppb "google.golang.org/protobuf/types/known/timestamppb" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type GetNameRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *GetNameRequest) Reset() { - *x = GetNameRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetNameRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetNameRequest) ProtoMessage() {} - -func (x *GetNameRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetNameRequest.ProtoReflect.Descriptor instead. -func (*GetNameRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{0} -} - -type GetNameResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` -} - -func (x *GetNameResponse) Reset() { - *x = GetNameResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetNameResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetNameResponse) ProtoMessage() {} - -func (x *GetNameResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetNameResponse.ProtoReflect.Descriptor instead. -func (*GetNameResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{1} -} - -func (x *GetNameResponse) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -type GenerateDestinationRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Config *Configs `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` - Assets *Assets `protobuf:"bytes,2,opt,name=assets,proto3" json:"assets,omitempty"` - // Deprecated: Do not use. - Project *v1beta1.ProjectSpecification `protobuf:"bytes,3,opt,name=project,proto3" json:"project,omitempty"` - Options *PluginOptions `protobuf:"bytes,40,opt,name=options,proto3" json:"options,omitempty"` -} - -func (x *GenerateDestinationRequest) Reset() { - *x = GenerateDestinationRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GenerateDestinationRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GenerateDestinationRequest) ProtoMessage() {} - -func (x *GenerateDestinationRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GenerateDestinationRequest.ProtoReflect.Descriptor instead. -func (*GenerateDestinationRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{2} -} - -func (x *GenerateDestinationRequest) GetConfig() *Configs { - if x != nil { - return x.Config - } - return nil -} - -func (x *GenerateDestinationRequest) GetAssets() *Assets { - if x != nil { - return x.Assets - } - return nil -} - -// Deprecated: Do not use. -func (x *GenerateDestinationRequest) GetProject() *v1beta1.ProjectSpecification { - if x != nil { - return x.Project - } - return nil -} - -func (x *GenerateDestinationRequest) GetOptions() *PluginOptions { - if x != nil { - return x.Options - } - return nil -} - -type GenerateDestinationResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Destination string `protobuf:"bytes,1,opt,name=destination,proto3" json:"destination,omitempty"` - DestinationType string `protobuf:"bytes,2,opt,name=destination_type,json=destinationType,proto3" json:"destination_type,omitempty"` -} - -func (x *GenerateDestinationResponse) Reset() { - *x = GenerateDestinationResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GenerateDestinationResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GenerateDestinationResponse) ProtoMessage() {} - -func (x *GenerateDestinationResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GenerateDestinationResponse.ProtoReflect.Descriptor instead. -func (*GenerateDestinationResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{3} -} - -func (x *GenerateDestinationResponse) GetDestination() string { - if x != nil { - return x.Destination - } - return "" -} - -func (x *GenerateDestinationResponse) GetDestinationType() string { - if x != nil { - return x.DestinationType - } - return "" -} - -type GenerateDependenciesRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Config *Configs `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` - Assets *Assets `protobuf:"bytes,2,opt,name=assets,proto3" json:"assets,omitempty"` - // Deprecated: Do not use. - Project *v1beta1.ProjectSpecification `protobuf:"bytes,3,opt,name=project,proto3" json:"project,omitempty"` - Options *PluginOptions `protobuf:"bytes,40,opt,name=options,proto3" json:"options,omitempty"` -} - -func (x *GenerateDependenciesRequest) Reset() { - *x = GenerateDependenciesRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GenerateDependenciesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GenerateDependenciesRequest) ProtoMessage() {} - -func (x *GenerateDependenciesRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GenerateDependenciesRequest.ProtoReflect.Descriptor instead. -func (*GenerateDependenciesRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{4} -} - -func (x *GenerateDependenciesRequest) GetConfig() *Configs { - if x != nil { - return x.Config - } - return nil -} - -func (x *GenerateDependenciesRequest) GetAssets() *Assets { - if x != nil { - return x.Assets - } - return nil -} - -// Deprecated: Do not use. -func (x *GenerateDependenciesRequest) GetProject() *v1beta1.ProjectSpecification { - if x != nil { - return x.Project - } - return nil -} - -func (x *GenerateDependenciesRequest) GetOptions() *PluginOptions { - if x != nil { - return x.Options - } - return nil -} - -type GenerateDependenciesResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Dependencies []string `protobuf:"bytes,1,rep,name=dependencies,proto3" json:"dependencies,omitempty"` -} - -func (x *GenerateDependenciesResponse) Reset() { - *x = GenerateDependenciesResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GenerateDependenciesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GenerateDependenciesResponse) ProtoMessage() {} - -func (x *GenerateDependenciesResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GenerateDependenciesResponse.ProtoReflect.Descriptor instead. -func (*GenerateDependenciesResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{5} -} - -func (x *GenerateDependenciesResponse) GetDependencies() []string { - if x != nil { - return x.Dependencies - } - return nil -} - -type Configs struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Configs []*Configs_Config `protobuf:"bytes,1,rep,name=configs,proto3" json:"configs,omitempty"` -} - -func (x *Configs) Reset() { - *x = Configs{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Configs) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Configs) ProtoMessage() {} - -func (x *Configs) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Configs.ProtoReflect.Descriptor instead. -func (*Configs) Descriptor() ([]byte, []int) { - return file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{6} -} - -func (x *Configs) GetConfigs() []*Configs_Config { - if x != nil { - return x.Configs - } - return nil -} - -type Assets struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Assets []*Assets_Asset `protobuf:"bytes,1,rep,name=assets,proto3" json:"assets,omitempty"` -} - -func (x *Assets) Reset() { - *x = Assets{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Assets) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Assets) ProtoMessage() {} - -func (x *Assets) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Assets.ProtoReflect.Descriptor instead. -func (*Assets) Descriptor() ([]byte, []int) { - return file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{7} -} - -func (x *Assets) GetAssets() []*Assets_Asset { - if x != nil { - return x.Assets - } - return nil -} - -type CompileAssetsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Configs *Configs `protobuf:"bytes,1,opt,name=configs,proto3" json:"configs,omitempty"` - Assets *Assets `protobuf:"bytes,2,opt,name=assets,proto3" json:"assets,omitempty"` - // Deprecated: Do not use. - Window *v1beta1.TaskWindow `protobuf:"bytes,3,opt,name=window,proto3" json:"window,omitempty"` - // Deprecated: Do not use. - InstanceSchedule *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=instance_schedule,json=instanceSchedule,proto3" json:"instance_schedule,omitempty"` - InstanceData []*v1beta1.InstanceSpecData `protobuf:"bytes,5,rep,name=instance_data,json=instanceData,proto3" json:"instance_data,omitempty"` - StartTime *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` - EndTime *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` - Options *PluginOptions `protobuf:"bytes,40,opt,name=options,proto3" json:"options,omitempty"` -} - -func (x *CompileAssetsRequest) Reset() { - *x = CompileAssetsRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CompileAssetsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CompileAssetsRequest) ProtoMessage() {} - -func (x *CompileAssetsRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CompileAssetsRequest.ProtoReflect.Descriptor instead. -func (*CompileAssetsRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{8} -} - -func (x *CompileAssetsRequest) GetConfigs() *Configs { - if x != nil { - return x.Configs - } - return nil -} - -func (x *CompileAssetsRequest) GetAssets() *Assets { - if x != nil { - return x.Assets - } - return nil -} - -// Deprecated: Do not use. -func (x *CompileAssetsRequest) GetWindow() *v1beta1.TaskWindow { - if x != nil { - return x.Window - } - return nil -} - -// Deprecated: Do not use. -func (x *CompileAssetsRequest) GetInstanceSchedule() *timestamppb.Timestamp { - if x != nil { - return x.InstanceSchedule - } - return nil -} - -func (x *CompileAssetsRequest) GetInstanceData() []*v1beta1.InstanceSpecData { - if x != nil { - return x.InstanceData - } - return nil -} - -func (x *CompileAssetsRequest) GetStartTime() *timestamppb.Timestamp { - if x != nil { - return x.StartTime - } - return nil -} - -func (x *CompileAssetsRequest) GetEndTime() *timestamppb.Timestamp { - if x != nil { - return x.EndTime - } - return nil -} - -func (x *CompileAssetsRequest) GetOptions() *PluginOptions { - if x != nil { - return x.Options - } - return nil -} - -type CompileAssetsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Assets *Assets `protobuf:"bytes,1,opt,name=assets,proto3" json:"assets,omitempty"` -} - -func (x *CompileAssetsResponse) Reset() { - *x = CompileAssetsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CompileAssetsResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CompileAssetsResponse) ProtoMessage() {} - -func (x *CompileAssetsResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CompileAssetsResponse.ProtoReflect.Descriptor instead. -func (*CompileAssetsResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{9} -} - -func (x *CompileAssetsResponse) GetAssets() *Assets { - if x != nil { - return x.Assets - } - return nil -} - -type PluginOptions struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - DryRun bool `protobuf:"varint,1,opt,name=dry_run,json=dryRun,proto3" json:"dry_run,omitempty"` -} - -func (x *PluginOptions) Reset() { - *x = PluginOptions{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *PluginOptions) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PluginOptions) ProtoMessage() {} - -func (x *PluginOptions) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[10] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PluginOptions.ProtoReflect.Descriptor instead. -func (*PluginOptions) Descriptor() ([]byte, []int) { - return file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{10} -} - -func (x *PluginOptions) GetDryRun() bool { - if x != nil { - return x.DryRun - } - return false -} - -type Configs_Config struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` -} - -func (x *Configs_Config) Reset() { - *x = Configs_Config{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Configs_Config) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Configs_Config) ProtoMessage() {} - -func (x *Configs_Config) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[11] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Configs_Config.ProtoReflect.Descriptor instead. -func (*Configs_Config) Descriptor() ([]byte, []int) { - return file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{6, 0} -} - -func (x *Configs_Config) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *Configs_Config) GetValue() string { - if x != nil { - return x.Value - } - return "" -} - -type Assets_Asset struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` -} - -func (x *Assets_Asset) Reset() { - *x = Assets_Asset{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Assets_Asset) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Assets_Asset) ProtoMessage() {} - -func (x *Assets_Asset) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[12] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Assets_Asset.ProtoReflect.Descriptor instead. -func (*Assets_Asset) Descriptor() ([]byte, []int) { - return file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{7, 0} -} - -func (x *Assets_Asset) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *Assets_Asset) GetValue() string { - if x != nil { - return x.Value - } - return "" -} - -var File_odpf_optimus_plugins_v1beta1_dependency_resolver_proto protoreflect.FileDescriptor - -var file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_rawDesc = []byte{ - 0x0a, 0x36, 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x64, - 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, - 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, - 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x27, 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x6f, 0x70, - 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2f, 0x6a, 0x6f, 0x62, 0x5f, 0x72, 0x75, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x1a, 0x27, 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, - 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x10, 0x0a, 0x0e, 0x47, 0x65, 0x74, - 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x25, 0x0a, 0x0f, 0x47, - 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x22, 0xaf, 0x02, 0x0a, 0x1a, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x44, - 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x3d, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x25, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, - 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x12, 0x3c, 0x0a, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x24, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x4d, - 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x2f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x45, 0x0a, - 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, - 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x6a, 0x0a, 0x1b, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, - 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, - 0x22, 0xb0, 0x02, 0x0a, 0x1b, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, - 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x3d, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x25, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, - 0x3c, 0x0a, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x24, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x4d, 0x0a, - 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, - 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, - 0x02, 0x18, 0x01, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x45, 0x0a, 0x07, - 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, - 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x22, 0x42, 0x0a, 0x1c, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x44, - 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, - 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x64, 0x65, 0x70, 0x65, 0x6e, - 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x22, 0x85, 0x01, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x73, 0x12, 0x46, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, - 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x1a, 0x32, 0x0a, 0x06, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, - 0x7f, 0x0a, 0x06, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x42, 0x0a, 0x06, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x6f, 0x64, 0x70, 0x66, - 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2e, - 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x1a, 0x31, 0x0a, - 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x22, 0xb0, 0x04, 0x0a, 0x14, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x07, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6f, 0x64, 0x70, - 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x73, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x3c, 0x0a, 0x06, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6f, 0x64, 0x70, - 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, - 0x52, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x41, 0x0a, 0x06, 0x77, 0x69, 0x6e, 0x64, - 0x6f, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, - 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x42, - 0x02, 0x18, 0x01, 0x52, 0x06, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, 0x4b, 0x0a, 0x11, 0x69, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x42, 0x02, 0x18, 0x01, 0x52, 0x10, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, - 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x50, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x2b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0c, 0x69, 0x6e, - 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, - 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x45, 0x0a, 0x07, - 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, - 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x22, 0x55, 0x0a, 0x15, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x41, 0x73, - 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x06, - 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6f, - 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x73, 0x52, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x22, 0x28, 0x0a, 0x0d, 0x50, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x64, - 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x72, - 0x79, 0x52, 0x75, 0x6e, 0x32, 0x9d, 0x04, 0x0a, 0x1c, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, - 0x6e, 0x63, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x66, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, - 0x12, 0x2c, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, - 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x8a, 0x01, - 0x0a, 0x13, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, - 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x44, 0x65, 0x73, - 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x39, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x8d, 0x01, 0x0a, 0x14, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, - 0x69, 0x65, 0x73, 0x12, 0x39, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, - 0x75, 0x73, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, - 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3a, - 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x78, 0x0a, 0x0d, 0x43, 0x6f, - 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x32, 0x2e, 0x6f, 0x64, - 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x69, - 0x6c, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x33, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, - 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x5e, 0x0a, 0x1e, 0x69, 0x6f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x42, 0x1a, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, - 0x63, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x1e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, 0x6f, 0x70, 0x74, - 0x69, 0x6d, 0x75, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescOnce sync.Once - file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescData = file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_rawDesc -) - -func file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP() []byte { - file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescOnce.Do(func() { - file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescData = protoimpl.X.CompressGZIP(file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescData) - }) - return file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescData -} - -var file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes = make([]protoimpl.MessageInfo, 13) -var file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_goTypes = []interface{}{ - (*GetNameRequest)(nil), // 0: odpf.optimus.plugins.v1beta1.GetNameRequest - (*GetNameResponse)(nil), // 1: odpf.optimus.plugins.v1beta1.GetNameResponse - (*GenerateDestinationRequest)(nil), // 2: odpf.optimus.plugins.v1beta1.GenerateDestinationRequest - (*GenerateDestinationResponse)(nil), // 3: odpf.optimus.plugins.v1beta1.GenerateDestinationResponse - (*GenerateDependenciesRequest)(nil), // 4: odpf.optimus.plugins.v1beta1.GenerateDependenciesRequest - (*GenerateDependenciesResponse)(nil), // 5: odpf.optimus.plugins.v1beta1.GenerateDependenciesResponse - (*Configs)(nil), // 6: odpf.optimus.plugins.v1beta1.Configs - (*Assets)(nil), // 7: odpf.optimus.plugins.v1beta1.Assets - (*CompileAssetsRequest)(nil), // 8: odpf.optimus.plugins.v1beta1.CompileAssetsRequest - (*CompileAssetsResponse)(nil), // 9: odpf.optimus.plugins.v1beta1.CompileAssetsResponse - (*PluginOptions)(nil), // 10: odpf.optimus.plugins.v1beta1.PluginOptions - (*Configs_Config)(nil), // 11: odpf.optimus.plugins.v1beta1.Configs.Config - (*Assets_Asset)(nil), // 12: odpf.optimus.plugins.v1beta1.Assets.Asset - (*v1beta1.ProjectSpecification)(nil), // 13: odpf.optimus.core.v1beta1.ProjectSpecification - (*v1beta1.TaskWindow)(nil), // 14: odpf.optimus.core.v1beta1.TaskWindow - (*timestamppb.Timestamp)(nil), // 15: google.protobuf.Timestamp - (*v1beta1.InstanceSpecData)(nil), // 16: odpf.optimus.core.v1beta1.InstanceSpecData -} -var file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_depIdxs = []int32{ - 6, // 0: odpf.optimus.plugins.v1beta1.GenerateDestinationRequest.config:type_name -> odpf.optimus.plugins.v1beta1.Configs - 7, // 1: odpf.optimus.plugins.v1beta1.GenerateDestinationRequest.assets:type_name -> odpf.optimus.plugins.v1beta1.Assets - 13, // 2: odpf.optimus.plugins.v1beta1.GenerateDestinationRequest.project:type_name -> odpf.optimus.core.v1beta1.ProjectSpecification - 10, // 3: odpf.optimus.plugins.v1beta1.GenerateDestinationRequest.options:type_name -> odpf.optimus.plugins.v1beta1.PluginOptions - 6, // 4: odpf.optimus.plugins.v1beta1.GenerateDependenciesRequest.config:type_name -> odpf.optimus.plugins.v1beta1.Configs - 7, // 5: odpf.optimus.plugins.v1beta1.GenerateDependenciesRequest.assets:type_name -> odpf.optimus.plugins.v1beta1.Assets - 13, // 6: odpf.optimus.plugins.v1beta1.GenerateDependenciesRequest.project:type_name -> odpf.optimus.core.v1beta1.ProjectSpecification - 10, // 7: odpf.optimus.plugins.v1beta1.GenerateDependenciesRequest.options:type_name -> odpf.optimus.plugins.v1beta1.PluginOptions - 11, // 8: odpf.optimus.plugins.v1beta1.Configs.configs:type_name -> odpf.optimus.plugins.v1beta1.Configs.Config - 12, // 9: odpf.optimus.plugins.v1beta1.Assets.assets:type_name -> odpf.optimus.plugins.v1beta1.Assets.Asset - 6, // 10: odpf.optimus.plugins.v1beta1.CompileAssetsRequest.configs:type_name -> odpf.optimus.plugins.v1beta1.Configs - 7, // 11: odpf.optimus.plugins.v1beta1.CompileAssetsRequest.assets:type_name -> odpf.optimus.plugins.v1beta1.Assets - 14, // 12: odpf.optimus.plugins.v1beta1.CompileAssetsRequest.window:type_name -> odpf.optimus.core.v1beta1.TaskWindow - 15, // 13: odpf.optimus.plugins.v1beta1.CompileAssetsRequest.instance_schedule:type_name -> google.protobuf.Timestamp - 16, // 14: odpf.optimus.plugins.v1beta1.CompileAssetsRequest.instance_data:type_name -> odpf.optimus.core.v1beta1.InstanceSpecData - 15, // 15: odpf.optimus.plugins.v1beta1.CompileAssetsRequest.start_time:type_name -> google.protobuf.Timestamp - 15, // 16: odpf.optimus.plugins.v1beta1.CompileAssetsRequest.end_time:type_name -> google.protobuf.Timestamp - 10, // 17: odpf.optimus.plugins.v1beta1.CompileAssetsRequest.options:type_name -> odpf.optimus.plugins.v1beta1.PluginOptions - 7, // 18: odpf.optimus.plugins.v1beta1.CompileAssetsResponse.assets:type_name -> odpf.optimus.plugins.v1beta1.Assets - 0, // 19: odpf.optimus.plugins.v1beta1.DependencyResolverModService.GetName:input_type -> odpf.optimus.plugins.v1beta1.GetNameRequest - 2, // 20: odpf.optimus.plugins.v1beta1.DependencyResolverModService.GenerateDestination:input_type -> odpf.optimus.plugins.v1beta1.GenerateDestinationRequest - 4, // 21: odpf.optimus.plugins.v1beta1.DependencyResolverModService.GenerateDependencies:input_type -> odpf.optimus.plugins.v1beta1.GenerateDependenciesRequest - 8, // 22: odpf.optimus.plugins.v1beta1.DependencyResolverModService.CompileAssets:input_type -> odpf.optimus.plugins.v1beta1.CompileAssetsRequest - 1, // 23: odpf.optimus.plugins.v1beta1.DependencyResolverModService.GetName:output_type -> odpf.optimus.plugins.v1beta1.GetNameResponse - 3, // 24: odpf.optimus.plugins.v1beta1.DependencyResolverModService.GenerateDestination:output_type -> odpf.optimus.plugins.v1beta1.GenerateDestinationResponse - 5, // 25: odpf.optimus.plugins.v1beta1.DependencyResolverModService.GenerateDependencies:output_type -> odpf.optimus.plugins.v1beta1.GenerateDependenciesResponse - 9, // 26: odpf.optimus.plugins.v1beta1.DependencyResolverModService.CompileAssets:output_type -> odpf.optimus.plugins.v1beta1.CompileAssetsResponse - 23, // [23:27] is the sub-list for method output_type - 19, // [19:23] is the sub-list for method input_type - 19, // [19:19] is the sub-list for extension type_name - 19, // [19:19] is the sub-list for extension extendee - 0, // [0:19] is the sub-list for field type_name -} - -func init() { file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_init() } -func file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_init() { - if File_odpf_optimus_plugins_v1beta1_dependency_resolver_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetNameRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetNameResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GenerateDestinationRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GenerateDestinationResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GenerateDependenciesRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GenerateDependenciesResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Configs); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Assets); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CompileAssetsRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CompileAssetsResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PluginOptions); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Configs_Config); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Assets_Asset); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_rawDesc, - NumEnums: 0, - NumMessages: 13, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_goTypes, - DependencyIndexes: file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_depIdxs, - MessageInfos: file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes, - }.Build() - File_odpf_optimus_plugins_v1beta1_dependency_resolver_proto = out.File - file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_rawDesc = nil - file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_goTypes = nil - file_odpf_optimus_plugins_v1beta1_dependency_resolver_proto_depIdxs = nil -} diff --git a/protos/raystack/optimus/core/v1beta1/backup.pb.go b/protos/raystack/optimus/core/v1beta1/backup.pb.go new file mode 100644 index 0000000000..e7d99c6102 --- /dev/null +++ b/protos/raystack/optimus/core/v1beta1/backup.pb.go @@ -0,0 +1,865 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.0 +// protoc (unknown) +// source: raystack/optimus/core/v1beta1/backup.proto + +package optimus + +import ( + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type IgnoredResource struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Reason string `protobuf:"bytes,2,opt,name=reason,proto3" json:"reason,omitempty"` +} + +func (x *IgnoredResource) Reset() { + *x = IgnoredResource{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_backup_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IgnoredResource) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IgnoredResource) ProtoMessage() {} + +func (x *IgnoredResource) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_backup_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IgnoredResource.ProtoReflect.Descriptor instead. +func (*IgnoredResource) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_backup_proto_rawDescGZIP(), []int{0} +} + +func (x *IgnoredResource) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *IgnoredResource) GetReason() string { + if x != nil { + return x.Reason + } + return "" +} + +type CreateBackupRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + DatastoreName string `protobuf:"bytes,2,opt,name=datastore_name,json=datastoreName,proto3" json:"datastore_name,omitempty"` + NamespaceName string `protobuf:"bytes,4,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` + Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty"` + Config map[string]string `protobuf:"bytes,7,rep,name=config,proto3" json:"config,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + ResourceNames []string `protobuf:"bytes,9,rep,name=resource_names,json=resourceNames,proto3" json:"resource_names,omitempty"` +} + +func (x *CreateBackupRequest) Reset() { + *x = CreateBackupRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_backup_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateBackupRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateBackupRequest) ProtoMessage() {} + +func (x *CreateBackupRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_backup_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateBackupRequest.ProtoReflect.Descriptor instead. +func (*CreateBackupRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_backup_proto_rawDescGZIP(), []int{1} +} + +func (x *CreateBackupRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *CreateBackupRequest) GetDatastoreName() string { + if x != nil { + return x.DatastoreName + } + return "" +} + +func (x *CreateBackupRequest) GetNamespaceName() string { + if x != nil { + return x.NamespaceName + } + return "" +} + +func (x *CreateBackupRequest) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *CreateBackupRequest) GetConfig() map[string]string { + if x != nil { + return x.Config + } + return nil +} + +func (x *CreateBackupRequest) GetResourceNames() []string { + if x != nil { + return x.ResourceNames + } + return nil +} + +type CreateBackupResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ResourceNames []string `protobuf:"bytes,1,rep,name=resource_names,json=resourceNames,proto3" json:"resource_names,omitempty"` + IgnoredResources []*IgnoredResource `protobuf:"bytes,3,rep,name=ignored_resources,json=ignoredResources,proto3" json:"ignored_resources,omitempty"` + BackupId string `protobuf:"bytes,4,opt,name=backup_id,json=backupId,proto3" json:"backup_id,omitempty"` +} + +func (x *CreateBackupResponse) Reset() { + *x = CreateBackupResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_backup_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateBackupResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateBackupResponse) ProtoMessage() {} + +func (x *CreateBackupResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_backup_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateBackupResponse.ProtoReflect.Descriptor instead. +func (*CreateBackupResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_backup_proto_rawDescGZIP(), []int{2} +} + +func (x *CreateBackupResponse) GetResourceNames() []string { + if x != nil { + return x.ResourceNames + } + return nil +} + +func (x *CreateBackupResponse) GetIgnoredResources() []*IgnoredResource { + if x != nil { + return x.IgnoredResources + } + return nil +} + +func (x *CreateBackupResponse) GetBackupId() string { + if x != nil { + return x.BackupId + } + return "" +} + +type ListBackupsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + DatastoreName string `protobuf:"bytes,2,opt,name=datastore_name,json=datastoreName,proto3" json:"datastore_name,omitempty"` + NamespaceName string `protobuf:"bytes,3,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` +} + +func (x *ListBackupsRequest) Reset() { + *x = ListBackupsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_backup_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListBackupsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListBackupsRequest) ProtoMessage() {} + +func (x *ListBackupsRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_backup_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListBackupsRequest.ProtoReflect.Descriptor instead. +func (*ListBackupsRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_backup_proto_rawDescGZIP(), []int{3} +} + +func (x *ListBackupsRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *ListBackupsRequest) GetDatastoreName() string { + if x != nil { + return x.DatastoreName + } + return "" +} + +func (x *ListBackupsRequest) GetNamespaceName() string { + if x != nil { + return x.NamespaceName + } + return "" +} + +type ListBackupsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Backups []*BackupSpec `protobuf:"bytes,1,rep,name=backups,proto3" json:"backups,omitempty"` +} + +func (x *ListBackupsResponse) Reset() { + *x = ListBackupsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_backup_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListBackupsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListBackupsResponse) ProtoMessage() {} + +func (x *ListBackupsResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_backup_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListBackupsResponse.ProtoReflect.Descriptor instead. +func (*ListBackupsResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_backup_proto_rawDescGZIP(), []int{4} +} + +func (x *ListBackupsResponse) GetBackups() []*BackupSpec { + if x != nil { + return x.Backups + } + return nil +} + +type BackupSpec struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` + Config map[string]string `protobuf:"bytes,5,rep,name=config,proto3" json:"config,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + ResourceNames []string `protobuf:"bytes,6,rep,name=resource_names,json=resourceNames,proto3" json:"resource_names,omitempty"` +} + +func (x *BackupSpec) Reset() { + *x = BackupSpec{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_backup_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BackupSpec) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BackupSpec) ProtoMessage() {} + +func (x *BackupSpec) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_backup_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BackupSpec.ProtoReflect.Descriptor instead. +func (*BackupSpec) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_backup_proto_rawDescGZIP(), []int{5} +} + +func (x *BackupSpec) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *BackupSpec) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *BackupSpec) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *BackupSpec) GetConfig() map[string]string { + if x != nil { + return x.Config + } + return nil +} + +func (x *BackupSpec) GetResourceNames() []string { + if x != nil { + return x.ResourceNames + } + return nil +} + +type GetBackupRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + DatastoreName string `protobuf:"bytes,2,opt,name=datastore_name,json=datastoreName,proto3" json:"datastore_name,omitempty"` + NamespaceName string `protobuf:"bytes,3,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` + Id string `protobuf:"bytes,4,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *GetBackupRequest) Reset() { + *x = GetBackupRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_backup_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetBackupRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBackupRequest) ProtoMessage() {} + +func (x *GetBackupRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_backup_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetBackupRequest.ProtoReflect.Descriptor instead. +func (*GetBackupRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_backup_proto_rawDescGZIP(), []int{6} +} + +func (x *GetBackupRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *GetBackupRequest) GetDatastoreName() string { + if x != nil { + return x.DatastoreName + } + return "" +} + +func (x *GetBackupRequest) GetNamespaceName() string { + if x != nil { + return x.NamespaceName + } + return "" +} + +func (x *GetBackupRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type GetBackupResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Spec *BackupSpec `protobuf:"bytes,1,opt,name=spec,proto3" json:"spec,omitempty"` +} + +func (x *GetBackupResponse) Reset() { + *x = GetBackupResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_backup_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetBackupResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBackupResponse) ProtoMessage() {} + +func (x *GetBackupResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_backup_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetBackupResponse.ProtoReflect.Descriptor instead. +func (*GetBackupResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_backup_proto_rawDescGZIP(), []int{7} +} + +func (x *GetBackupResponse) GetSpec() *BackupSpec { + if x != nil { + return x.Spec + } + return nil +} + +var File_raystack_optimus_core_v1beta1_backup_proto protoreflect.FileDescriptor + +var file_raystack_optimus_core_v1beta1_backup_proto_rawDesc = []byte{ + 0x0a, 0x2d, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2f, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x20, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, + 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, + 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, + 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x22, 0x3d, 0x0a, 0x0f, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, + 0xf7, 0x02, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x61, + 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x59, 0x0a, 0x06, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x67, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, + 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x1a, 0x39, 0x0a, 0x0b, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, + 0x06, 0x10, 0x07, 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x22, 0xc0, 0x01, 0x0a, 0x14, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x5e, 0x0a, 0x11, 0x69, 0x67, 0x6e, + 0x6f, 0x72, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x10, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x61, 0x63, + 0x6b, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x61, + 0x63, 0x6b, 0x75, 0x70, 0x49, 0x64, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0x85, 0x01, 0x0a, + 0x12, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, + 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, + 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x5d, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x07, 0x62, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x67, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x70, 0x65, 0x63, 0x52, 0x07, 0x62, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x73, 0x22, 0xb3, 0x02, 0x0a, 0x0a, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x70, + 0x65, 0x63, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x20, 0x0a, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x50, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x38, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0x93, 0x01, 0x0a, 0x10, 0x47, 0x65, + 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, + 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, + 0x5b, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x70, 0x65, 0x63, + 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x32, 0xb8, 0x05, 0x0a, + 0x0d, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xe6, + 0x01, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, + 0x35, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x67, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x61, 0x22, 0x5c, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x7d, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x7b, 0x64, 0x61, + 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x62, 0x61, + 0x63, 0x6b, 0x75, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0xe0, 0x01, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x34, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x42, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, + 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x64, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x5e, 0x12, 0x5c, 0x2f, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x2f, 0x7b, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x7d, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0xda, 0x01, 0x0a, 0x09, 0x47, + 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x42, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x67, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x47, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x64, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x5e, 0x12, 0x5c, 0x2f, 0x76, 0x31, 0x2f, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, + 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, + 0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x7b, 0x64, 0x61, 0x74, 0x61, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x62, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x42, 0x95, 0x01, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x6e, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x42, 0x14, 0x42, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, + 0x50, 0x01, 0x5a, 0x1e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, + 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x75, 0x73, 0x92, 0x41, 0x3a, 0x12, 0x05, 0x32, 0x03, 0x30, 0x2e, 0x31, 0x1a, 0x0e, 0x31, 0x32, + 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x3a, 0x39, 0x31, 0x30, 0x30, 0x22, 0x04, 0x2f, 0x61, + 0x70, 0x69, 0x2a, 0x01, 0x01, 0x72, 0x18, 0x0a, 0x16, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, + 0x20, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_raystack_optimus_core_v1beta1_backup_proto_rawDescOnce sync.Once + file_raystack_optimus_core_v1beta1_backup_proto_rawDescData = file_raystack_optimus_core_v1beta1_backup_proto_rawDesc +) + +func file_raystack_optimus_core_v1beta1_backup_proto_rawDescGZIP() []byte { + file_raystack_optimus_core_v1beta1_backup_proto_rawDescOnce.Do(func() { + file_raystack_optimus_core_v1beta1_backup_proto_rawDescData = protoimpl.X.CompressGZIP(file_raystack_optimus_core_v1beta1_backup_proto_rawDescData) + }) + return file_raystack_optimus_core_v1beta1_backup_proto_rawDescData +} + +var file_raystack_optimus_core_v1beta1_backup_proto_msgTypes = make([]protoimpl.MessageInfo, 10) +var file_raystack_optimus_core_v1beta1_backup_proto_goTypes = []interface{}{ + (*IgnoredResource)(nil), // 0: raystack.optimus.core.v1beta1.IgnoredResource + (*CreateBackupRequest)(nil), // 1: raystack.optimus.core.v1beta1.CreateBackupRequest + (*CreateBackupResponse)(nil), // 2: raystack.optimus.core.v1beta1.CreateBackupResponse + (*ListBackupsRequest)(nil), // 3: raystack.optimus.core.v1beta1.ListBackupsRequest + (*ListBackupsResponse)(nil), // 4: raystack.optimus.core.v1beta1.ListBackupsResponse + (*BackupSpec)(nil), // 5: raystack.optimus.core.v1beta1.BackupSpec + (*GetBackupRequest)(nil), // 6: raystack.optimus.core.v1beta1.GetBackupRequest + (*GetBackupResponse)(nil), // 7: raystack.optimus.core.v1beta1.GetBackupResponse + nil, // 8: raystack.optimus.core.v1beta1.CreateBackupRequest.ConfigEntry + nil, // 9: raystack.optimus.core.v1beta1.BackupSpec.ConfigEntry + (*timestamppb.Timestamp)(nil), // 10: google.protobuf.Timestamp +} +var file_raystack_optimus_core_v1beta1_backup_proto_depIdxs = []int32{ + 8, // 0: raystack.optimus.core.v1beta1.CreateBackupRequest.config:type_name -> raystack.optimus.core.v1beta1.CreateBackupRequest.ConfigEntry + 0, // 1: raystack.optimus.core.v1beta1.CreateBackupResponse.ignored_resources:type_name -> raystack.optimus.core.v1beta1.IgnoredResource + 5, // 2: raystack.optimus.core.v1beta1.ListBackupsResponse.backups:type_name -> raystack.optimus.core.v1beta1.BackupSpec + 10, // 3: raystack.optimus.core.v1beta1.BackupSpec.created_at:type_name -> google.protobuf.Timestamp + 9, // 4: raystack.optimus.core.v1beta1.BackupSpec.config:type_name -> raystack.optimus.core.v1beta1.BackupSpec.ConfigEntry + 5, // 5: raystack.optimus.core.v1beta1.GetBackupResponse.spec:type_name -> raystack.optimus.core.v1beta1.BackupSpec + 1, // 6: raystack.optimus.core.v1beta1.BackupService.CreateBackup:input_type -> raystack.optimus.core.v1beta1.CreateBackupRequest + 3, // 7: raystack.optimus.core.v1beta1.BackupService.ListBackups:input_type -> raystack.optimus.core.v1beta1.ListBackupsRequest + 6, // 8: raystack.optimus.core.v1beta1.BackupService.GetBackup:input_type -> raystack.optimus.core.v1beta1.GetBackupRequest + 2, // 9: raystack.optimus.core.v1beta1.BackupService.CreateBackup:output_type -> raystack.optimus.core.v1beta1.CreateBackupResponse + 4, // 10: raystack.optimus.core.v1beta1.BackupService.ListBackups:output_type -> raystack.optimus.core.v1beta1.ListBackupsResponse + 7, // 11: raystack.optimus.core.v1beta1.BackupService.GetBackup:output_type -> raystack.optimus.core.v1beta1.GetBackupResponse + 9, // [9:12] is the sub-list for method output_type + 6, // [6:9] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name +} + +func init() { file_raystack_optimus_core_v1beta1_backup_proto_init() } +func file_raystack_optimus_core_v1beta1_backup_proto_init() { + if File_raystack_optimus_core_v1beta1_backup_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_raystack_optimus_core_v1beta1_backup_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*IgnoredResource); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_backup_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateBackupRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_backup_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateBackupResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_backup_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListBackupsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_backup_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListBackupsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_backup_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BackupSpec); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_backup_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetBackupRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_backup_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetBackupResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_raystack_optimus_core_v1beta1_backup_proto_rawDesc, + NumEnums: 0, + NumMessages: 10, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_raystack_optimus_core_v1beta1_backup_proto_goTypes, + DependencyIndexes: file_raystack_optimus_core_v1beta1_backup_proto_depIdxs, + MessageInfos: file_raystack_optimus_core_v1beta1_backup_proto_msgTypes, + }.Build() + File_raystack_optimus_core_v1beta1_backup_proto = out.File + file_raystack_optimus_core_v1beta1_backup_proto_rawDesc = nil + file_raystack_optimus_core_v1beta1_backup_proto_goTypes = nil + file_raystack_optimus_core_v1beta1_backup_proto_depIdxs = nil +} diff --git a/protos/odpf/optimus/core/v1beta1/backup.pb.gw.go b/protos/raystack/optimus/core/v1beta1/backup.pb.gw.go similarity index 93% rename from protos/odpf/optimus/core/v1beta1/backup.pb.gw.go rename to protos/raystack/optimus/core/v1beta1/backup.pb.gw.go index d350f608d4..27d4907639 100644 --- a/protos/odpf/optimus/core/v1beta1/backup.pb.gw.go +++ b/protos/raystack/optimus/core/v1beta1/backup.pb.gw.go @@ -1,5 +1,5 @@ // Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. -// source: odpf/optimus/core/v1beta1/backup.proto +// source: raystack/optimus/core/v1beta1/backup.proto /* Package optimus is a reverse proxy. @@ -355,7 +355,7 @@ func RegisterBackupServiceHandlerServer(ctx context.Context, mux *runtime.ServeM var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.BackupService/CreateBackup", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/backup")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.BackupService/CreateBackup", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/backup")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -378,7 +378,7 @@ func RegisterBackupServiceHandlerServer(ctx context.Context, mux *runtime.ServeM var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.BackupService/ListBackups", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/backup")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.BackupService/ListBackups", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/backup")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -401,7 +401,7 @@ func RegisterBackupServiceHandlerServer(ctx context.Context, mux *runtime.ServeM var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.BackupService/GetBackup", runtime.WithHTTPPathPattern("/v1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/backup/{id}")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.BackupService/GetBackup", runtime.WithHTTPPathPattern("/v1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/backup/{id}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -463,7 +463,7 @@ func RegisterBackupServiceHandlerClient(ctx context.Context, mux *runtime.ServeM ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.BackupService/CreateBackup", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/backup")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.BackupService/CreateBackup", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/backup")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -483,7 +483,7 @@ func RegisterBackupServiceHandlerClient(ctx context.Context, mux *runtime.ServeM ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.BackupService/ListBackups", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/backup")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.BackupService/ListBackups", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/backup")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -503,7 +503,7 @@ func RegisterBackupServiceHandlerClient(ctx context.Context, mux *runtime.ServeM ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.BackupService/GetBackup", runtime.WithHTTPPathPattern("/v1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/backup/{id}")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.BackupService/GetBackup", runtime.WithHTTPPathPattern("/v1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/backup/{id}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return diff --git a/protos/odpf/optimus/core/v1beta1/backup.swagger.json b/protos/raystack/optimus/core/v1beta1/backup.swagger.json similarity index 94% rename from protos/odpf/optimus/core/v1beta1/backup.swagger.json rename to protos/raystack/optimus/core/v1beta1/backup.swagger.json index 45b1faa579..080073bd49 100644 --- a/protos/odpf/optimus/core/v1beta1/backup.swagger.json +++ b/protos/raystack/optimus/core/v1beta1/backup.swagger.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "title": "odpf/optimus/core/v1beta1/backup.proto", + "title": "raystack/optimus/core/v1beta1/backup.proto", "version": "0.1" }, "tags": [ @@ -11,15 +11,9 @@ ], "host": "127.0.0.1:9100", "basePath": "/api", - "schemes": [ - "http" - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], + "schemes": ["http"], + "consumes": ["application/json"], + "produces": ["application/json"], "paths": { "/v1/project/{projectName}/namespace/{namespaceName}/datastore/{datastoreName}/backup/{id}": { "get": { @@ -64,9 +58,7 @@ "type": "string" } ], - "tags": [ - "BackupService" - ] + "tags": ["BackupService"] } }, "/v1beta1/project/{projectName}/namespace/{namespaceName}/datastore/{datastoreName}/backup": { @@ -106,9 +98,7 @@ "type": "string" } ], - "tags": [ - "BackupService" - ] + "tags": ["BackupService"] }, "post": { "operationId": "BackupService_CreateBackup", @@ -171,9 +161,7 @@ } } ], - "tags": [ - "BackupService" - ] + "tags": ["BackupService"] } } }, diff --git a/protos/odpf/optimus/core/v1beta1/backup_grpc.pb.go b/protos/raystack/optimus/core/v1beta1/backup_grpc.pb.go similarity index 89% rename from protos/odpf/optimus/core/v1beta1/backup_grpc.pb.go rename to protos/raystack/optimus/core/v1beta1/backup_grpc.pb.go index 5659deceee..55b4bc0168 100644 --- a/protos/odpf/optimus/core/v1beta1/backup_grpc.pb.go +++ b/protos/raystack/optimus/core/v1beta1/backup_grpc.pb.go @@ -2,7 +2,7 @@ // versions: // - protoc-gen-go-grpc v1.2.0 // - protoc (unknown) -// source: odpf/optimus/core/v1beta1/backup.proto +// source: raystack/optimus/core/v1beta1/backup.proto package optimus @@ -37,7 +37,7 @@ func NewBackupServiceClient(cc grpc.ClientConnInterface) BackupServiceClient { func (c *backupServiceClient) CreateBackup(ctx context.Context, in *CreateBackupRequest, opts ...grpc.CallOption) (*CreateBackupResponse, error) { out := new(CreateBackupResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.BackupService/CreateBackup", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.BackupService/CreateBackup", in, out, opts...) if err != nil { return nil, err } @@ -46,7 +46,7 @@ func (c *backupServiceClient) CreateBackup(ctx context.Context, in *CreateBackup func (c *backupServiceClient) ListBackups(ctx context.Context, in *ListBackupsRequest, opts ...grpc.CallOption) (*ListBackupsResponse, error) { out := new(ListBackupsResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.BackupService/ListBackups", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.BackupService/ListBackups", in, out, opts...) if err != nil { return nil, err } @@ -55,7 +55,7 @@ func (c *backupServiceClient) ListBackups(ctx context.Context, in *ListBackupsRe func (c *backupServiceClient) GetBackup(ctx context.Context, in *GetBackupRequest, opts ...grpc.CallOption) (*GetBackupResponse, error) { out := new(GetBackupResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.BackupService/GetBackup", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.BackupService/GetBackup", in, out, opts...) if err != nil { return nil, err } @@ -108,7 +108,7 @@ func _BackupService_CreateBackup_Handler(srv interface{}, ctx context.Context, d } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.BackupService/CreateBackup", + FullMethod: "/raystack.optimus.core.v1beta1.BackupService/CreateBackup", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(BackupServiceServer).CreateBackup(ctx, req.(*CreateBackupRequest)) @@ -126,7 +126,7 @@ func _BackupService_ListBackups_Handler(srv interface{}, ctx context.Context, de } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.BackupService/ListBackups", + FullMethod: "/raystack.optimus.core.v1beta1.BackupService/ListBackups", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(BackupServiceServer).ListBackups(ctx, req.(*ListBackupsRequest)) @@ -144,7 +144,7 @@ func _BackupService_GetBackup_Handler(srv interface{}, ctx context.Context, dec } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.BackupService/GetBackup", + FullMethod: "/raystack.optimus.core.v1beta1.BackupService/GetBackup", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(BackupServiceServer).GetBackup(ctx, req.(*GetBackupRequest)) @@ -156,7 +156,7 @@ func _BackupService_GetBackup_Handler(srv interface{}, ctx context.Context, dec // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var BackupService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "odpf.optimus.core.v1beta1.BackupService", + ServiceName: "raystack.optimus.core.v1beta1.BackupService", HandlerType: (*BackupServiceServer)(nil), Methods: []grpc.MethodDesc{ { @@ -173,5 +173,5 @@ var BackupService_ServiceDesc = grpc.ServiceDesc{ }, }, Streams: []grpc.StreamDesc{}, - Metadata: "odpf/optimus/core/v1beta1/backup.proto", + Metadata: "raystack/optimus/core/v1beta1/backup.proto", } diff --git a/protos/raystack/optimus/core/v1beta1/job_run.pb.go b/protos/raystack/optimus/core/v1beta1/job_run.pb.go new file mode 100644 index 0000000000..4ba1cf2d21 --- /dev/null +++ b/protos/raystack/optimus/core/v1beta1/job_run.pb.go @@ -0,0 +1,1277 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.0 +// protoc (unknown) +// source: raystack/optimus/core/v1beta1/job_run.proto + +package optimus + +import ( + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + durationpb "google.golang.org/protobuf/types/known/durationpb" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type InstanceSpec_Type int32 + +const ( + InstanceSpec_TYPE_UNSPECIFIED InstanceSpec_Type = 0 + InstanceSpec_TYPE_TASK InstanceSpec_Type = 1 + InstanceSpec_TYPE_HOOK InstanceSpec_Type = 2 +) + +// Enum value maps for InstanceSpec_Type. +var ( + InstanceSpec_Type_name = map[int32]string{ + 0: "TYPE_UNSPECIFIED", + 1: "TYPE_TASK", + 2: "TYPE_HOOK", + } + InstanceSpec_Type_value = map[string]int32{ + "TYPE_UNSPECIFIED": 0, + "TYPE_TASK": 1, + "TYPE_HOOK": 2, + } +) + +func (x InstanceSpec_Type) Enum() *InstanceSpec_Type { + p := new(InstanceSpec_Type) + *p = x + return p +} + +func (x InstanceSpec_Type) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (InstanceSpec_Type) Descriptor() protoreflect.EnumDescriptor { + return file_raystack_optimus_core_v1beta1_job_run_proto_enumTypes[0].Descriptor() +} + +func (InstanceSpec_Type) Type() protoreflect.EnumType { + return &file_raystack_optimus_core_v1beta1_job_run_proto_enumTypes[0] +} + +func (x InstanceSpec_Type) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use InstanceSpec_Type.Descriptor instead. +func (InstanceSpec_Type) EnumDescriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{7, 0} +} + +// type of data, could be an env var or file +type InstanceSpecData_Type int32 + +const ( + InstanceSpecData_TYPE_UNSPECIFIED InstanceSpecData_Type = 0 + InstanceSpecData_TYPE_ENV InstanceSpecData_Type = 1 + InstanceSpecData_TYPE_FILE InstanceSpecData_Type = 2 +) + +// Enum value maps for InstanceSpecData_Type. +var ( + InstanceSpecData_Type_name = map[int32]string{ + 0: "TYPE_UNSPECIFIED", + 1: "TYPE_ENV", + 2: "TYPE_FILE", + } + InstanceSpecData_Type_value = map[string]int32{ + "TYPE_UNSPECIFIED": 0, + "TYPE_ENV": 1, + "TYPE_FILE": 2, + } +) + +func (x InstanceSpecData_Type) Enum() *InstanceSpecData_Type { + p := new(InstanceSpecData_Type) + *p = x + return p +} + +func (x InstanceSpecData_Type) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (InstanceSpecData_Type) Descriptor() protoreflect.EnumDescriptor { + return file_raystack_optimus_core_v1beta1_job_run_proto_enumTypes[1].Descriptor() +} + +func (InstanceSpecData_Type) Type() protoreflect.EnumType { + return &file_raystack_optimus_core_v1beta1_job_run_proto_enumTypes[1] +} + +func (x InstanceSpecData_Type) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use InstanceSpecData_Type.Descriptor instead. +func (InstanceSpecData_Type) EnumDescriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{8, 0} +} + +type UploadToSchedulerRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + NamespaceName *string `protobuf:"bytes,2,opt,name=namespace_name,json=namespaceName,proto3,oneof" json:"namespace_name,omitempty"` +} + +func (x *UploadToSchedulerRequest) Reset() { + *x = UploadToSchedulerRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UploadToSchedulerRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UploadToSchedulerRequest) ProtoMessage() {} + +func (x *UploadToSchedulerRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UploadToSchedulerRequest.ProtoReflect.Descriptor instead. +func (*UploadToSchedulerRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{0} +} + +func (x *UploadToSchedulerRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *UploadToSchedulerRequest) GetNamespaceName() string { + if x != nil && x.NamespaceName != nil { + return *x.NamespaceName + } + return "" +} + +type UploadToSchedulerResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Status bool `protobuf:"varint,1,opt,name=status,proto3" json:"status,omitempty"` + ErrorMessage string `protobuf:"bytes,2,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"` +} + +func (x *UploadToSchedulerResponse) Reset() { + *x = UploadToSchedulerResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UploadToSchedulerResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UploadToSchedulerResponse) ProtoMessage() {} + +func (x *UploadToSchedulerResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UploadToSchedulerResponse.ProtoReflect.Descriptor instead. +func (*UploadToSchedulerResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{1} +} + +func (x *UploadToSchedulerResponse) GetStatus() bool { + if x != nil { + return x.Status + } + return false +} + +func (x *UploadToSchedulerResponse) GetErrorMessage() string { + if x != nil { + return x.ErrorMessage + } + return "" +} + +type RegisterJobEventRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + JobName string `protobuf:"bytes,2,opt,name=job_name,json=jobName,proto3" json:"job_name,omitempty"` + NamespaceName string `protobuf:"bytes,3,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` + Event *JobEvent `protobuf:"bytes,4,opt,name=event,proto3" json:"event,omitempty"` +} + +func (x *RegisterJobEventRequest) Reset() { + *x = RegisterJobEventRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisterJobEventRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterJobEventRequest) ProtoMessage() {} + +func (x *RegisterJobEventRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterJobEventRequest.ProtoReflect.Descriptor instead. +func (*RegisterJobEventRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{2} +} + +func (x *RegisterJobEventRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *RegisterJobEventRequest) GetJobName() string { + if x != nil { + return x.JobName + } + return "" +} + +func (x *RegisterJobEventRequest) GetNamespaceName() string { + if x != nil { + return x.NamespaceName + } + return "" +} + +func (x *RegisterJobEventRequest) GetEvent() *JobEvent { + if x != nil { + return x.Event + } + return nil +} + +type RegisterJobEventResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *RegisterJobEventResponse) Reset() { + *x = RegisterJobEventResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisterJobEventResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterJobEventResponse) ProtoMessage() {} + +func (x *RegisterJobEventResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterJobEventResponse.ProtoReflect.Descriptor instead. +func (*RegisterJobEventResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{3} +} + +type JobRunInputRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + JobName string `protobuf:"bytes,2,opt,name=job_name,json=jobName,proto3" json:"job_name,omitempty"` + ScheduledAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=scheduled_at,json=scheduledAt,proto3" json:"scheduled_at,omitempty"` + InstanceName string `protobuf:"bytes,5,opt,name=instance_name,json=instanceName,proto3" json:"instance_name,omitempty"` + InstanceType InstanceSpec_Type `protobuf:"varint,6,opt,name=instance_type,json=instanceType,proto3,enum=raystack.optimus.core.v1beta1.InstanceSpec_Type" json:"instance_type,omitempty"` + // either set job_name if this is a scheduled execution + // or set jobrun_id if this is a manual triggered execution + // and not really registered as a valid job + JobrunId string `protobuf:"bytes,7,opt,name=jobrun_id,json=jobrunId,proto3" json:"jobrun_id,omitempty"` +} + +func (x *JobRunInputRequest) Reset() { + *x = JobRunInputRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *JobRunInputRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JobRunInputRequest) ProtoMessage() {} + +func (x *JobRunInputRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JobRunInputRequest.ProtoReflect.Descriptor instead. +func (*JobRunInputRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{4} +} + +func (x *JobRunInputRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *JobRunInputRequest) GetJobName() string { + if x != nil { + return x.JobName + } + return "" +} + +func (x *JobRunInputRequest) GetScheduledAt() *timestamppb.Timestamp { + if x != nil { + return x.ScheduledAt + } + return nil +} + +func (x *JobRunInputRequest) GetInstanceName() string { + if x != nil { + return x.InstanceName + } + return "" +} + +func (x *JobRunInputRequest) GetInstanceType() InstanceSpec_Type { + if x != nil { + return x.InstanceType + } + return InstanceSpec_TYPE_UNSPECIFIED +} + +func (x *JobRunInputRequest) GetJobrunId() string { + if x != nil { + return x.JobrunId + } + return "" +} + +type JobRunRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + JobName string `protobuf:"bytes,2,opt,name=job_name,json=jobName,proto3" json:"job_name,omitempty"` + StartDate *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=start_date,json=startDate,proto3" json:"start_date,omitempty"` + EndDate *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=end_date,json=endDate,proto3" json:"end_date,omitempty"` + Filter []string `protobuf:"bytes,5,rep,name=filter,proto3" json:"filter,omitempty"` +} + +func (x *JobRunRequest) Reset() { + *x = JobRunRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *JobRunRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JobRunRequest) ProtoMessage() {} + +func (x *JobRunRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JobRunRequest.ProtoReflect.Descriptor instead. +func (*JobRunRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{5} +} + +func (x *JobRunRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *JobRunRequest) GetJobName() string { + if x != nil { + return x.JobName + } + return "" +} + +func (x *JobRunRequest) GetStartDate() *timestamppb.Timestamp { + if x != nil { + return x.StartDate + } + return nil +} + +func (x *JobRunRequest) GetEndDate() *timestamppb.Timestamp { + if x != nil { + return x.EndDate + } + return nil +} + +func (x *JobRunRequest) GetFilter() []string { + if x != nil { + return x.Filter + } + return nil +} + +type JobRunResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + JobRuns []*JobRun `protobuf:"bytes,1,rep,name=job_runs,json=jobRuns,proto3" json:"job_runs,omitempty"` +} + +func (x *JobRunResponse) Reset() { + *x = JobRunResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *JobRunResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JobRunResponse) ProtoMessage() {} + +func (x *JobRunResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JobRunResponse.ProtoReflect.Descriptor instead. +func (*JobRunResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{6} +} + +func (x *JobRunResponse) GetJobRuns() []*JobRun { + if x != nil { + return x.JobRuns + } + return nil +} + +type InstanceSpec struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + State string `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"` + Data []*InstanceSpecData `protobuf:"bytes,3,rep,name=data,proto3" json:"data,omitempty"` + ExecutedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=executed_at,json=executedAt,proto3" json:"executed_at,omitempty"` + Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"` + Type InstanceSpec_Type `protobuf:"varint,7,opt,name=type,proto3,enum=raystack.optimus.core.v1beta1.InstanceSpec_Type" json:"type,omitempty"` +} + +func (x *InstanceSpec) Reset() { + *x = InstanceSpec{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InstanceSpec) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InstanceSpec) ProtoMessage() {} + +func (x *InstanceSpec) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InstanceSpec.ProtoReflect.Descriptor instead. +func (*InstanceSpec) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{7} +} + +func (x *InstanceSpec) GetState() string { + if x != nil { + return x.State + } + return "" +} + +func (x *InstanceSpec) GetData() []*InstanceSpecData { + if x != nil { + return x.Data + } + return nil +} + +func (x *InstanceSpec) GetExecutedAt() *timestamppb.Timestamp { + if x != nil { + return x.ExecutedAt + } + return nil +} + +func (x *InstanceSpec) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *InstanceSpec) GetType() InstanceSpec_Type { + if x != nil { + return x.Type + } + return InstanceSpec_TYPE_UNSPECIFIED +} + +type InstanceSpecData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + Type InstanceSpecData_Type `protobuf:"varint,5,opt,name=type,proto3,enum=raystack.optimus.core.v1beta1.InstanceSpecData_Type" json:"type,omitempty"` +} + +func (x *InstanceSpecData) Reset() { + *x = InstanceSpecData{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InstanceSpecData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InstanceSpecData) ProtoMessage() {} + +func (x *InstanceSpecData) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InstanceSpecData.ProtoReflect.Descriptor instead. +func (*InstanceSpecData) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{8} +} + +func (x *InstanceSpecData) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *InstanceSpecData) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +func (x *InstanceSpecData) GetType() InstanceSpecData_Type { + if x != nil { + return x.Type + } + return InstanceSpecData_TYPE_UNSPECIFIED +} + +type JobRunInputResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Envs map[string]string `protobuf:"bytes,1,rep,name=envs,proto3" json:"envs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Files map[string]string `protobuf:"bytes,2,rep,name=files,proto3" json:"files,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Secrets map[string]string `protobuf:"bytes,3,rep,name=secrets,proto3" json:"secrets,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *JobRunInputResponse) Reset() { + *x = JobRunInputResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *JobRunInputResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JobRunInputResponse) ProtoMessage() {} + +func (x *JobRunInputResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JobRunInputResponse.ProtoReflect.Descriptor instead. +func (*JobRunInputResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{9} +} + +func (x *JobRunInputResponse) GetEnvs() map[string]string { + if x != nil { + return x.Envs + } + return nil +} + +func (x *JobRunInputResponse) GetFiles() map[string]string { + if x != nil { + return x.Files + } + return nil +} + +func (x *JobRunInputResponse) GetSecrets() map[string]string { + if x != nil { + return x.Secrets + } + return nil +} + +type TaskWindow struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Size *durationpb.Duration `protobuf:"bytes,1,opt,name=size,proto3" json:"size,omitempty"` + Offset *durationpb.Duration `protobuf:"bytes,2,opt,name=offset,proto3" json:"offset,omitempty"` + TruncateTo string `protobuf:"bytes,3,opt,name=truncate_to,json=truncateTo,proto3" json:"truncate_to,omitempty"` +} + +func (x *TaskWindow) Reset() { + *x = TaskWindow{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TaskWindow) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TaskWindow) ProtoMessage() {} + +func (x *TaskWindow) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TaskWindow.ProtoReflect.Descriptor instead. +func (*TaskWindow) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_job_run_proto_rawDescGZIP(), []int{10} +} + +func (x *TaskWindow) GetSize() *durationpb.Duration { + if x != nil { + return x.Size + } + return nil +} + +func (x *TaskWindow) GetOffset() *durationpb.Duration { + if x != nil { + return x.Offset + } + return nil +} + +func (x *TaskWindow) GetTruncateTo() string { + if x != nil { + return x.TruncateTo + } + return "" +} + +var File_raystack_optimus_core_v1beta1_job_run_proto protoreflect.FileDescriptor + +var file_raystack_optimus_core_v1beta1_job_run_proto_rawDesc = []byte{ + 0x0a, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2f, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2f, 0x6a, 0x6f, 0x62, 0x5f, 0x72, 0x75, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x20, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, + 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x1a, 0x2f, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2f, 0x6f, + 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2f, 0x6a, 0x6f, 0x62, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, + 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x22, 0x7c, 0x0a, 0x18, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x53, 0x63, + 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, + 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x2a, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0d, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x42, 0x11, 0x0a, + 0x0f, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x22, 0x58, 0x0a, 0x19, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x53, 0x63, 0x68, 0x65, + 0x64, 0x75, 0x6c, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xc0, 0x01, 0x0a, 0x17, 0x52, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4a, 0x6f, 0x62, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x40, 0x0a, 0x05, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x67, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, + 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, + 0x62, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x1a, 0x0a, + 0x18, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4a, 0x6f, 0x62, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xb3, 0x02, 0x0a, 0x12, 0x4a, 0x6f, + 0x62, 0x52, 0x75, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3d, + 0x0a, 0x0c, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x0b, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x41, 0x74, 0x12, 0x23, 0x0a, + 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x58, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x33, 0x2e, 0x67, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0c, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, + 0x6a, 0x6f, 0x62, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x6a, 0x6f, 0x62, 0x72, 0x75, 0x6e, 0x49, 0x64, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, + 0xd7, 0x01, 0x0a, 0x0d, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x74, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x65, 0x6e, + 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, + 0x65, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x05, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x55, 0x0a, 0x0e, 0x4a, 0x6f, 0x62, + 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x08, 0x6a, + 0x6f, 0x62, 0x5f, 0x72, 0x75, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, + 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x73, + 0x22, 0xce, 0x02, 0x0a, 0x0c, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x70, 0x65, + 0x63, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x46, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, + 0x65, 0x53, 0x70, 0x65, 0x63, 0x44, 0x61, 0x74, 0x61, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, + 0x3b, 0x0a, 0x0b, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x0a, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x47, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x33, + 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x54, + 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x0a, 0x04, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, + 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x54, 0x41, 0x53, 0x4b, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x48, + 0x4f, 0x4f, 0x4b, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x04, 0x10, + 0x05, 0x22, 0xd0, 0x01, 0x0a, 0x10, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x70, + 0x65, 0x63, 0x44, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x12, 0x4b, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x37, + 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x44, 0x61, + 0x74, 0x61, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x39, 0x0a, + 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, + 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x45, 0x4e, 0x56, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, + 0x08, 0x04, 0x10, 0x05, 0x22, 0xcf, 0x03, 0x0a, 0x13, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x49, + 0x6e, 0x70, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x04, + 0x65, 0x6e, 0x76, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x67, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, + 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, + 0x62, 0x52, 0x75, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x2e, 0x45, 0x6e, 0x76, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x65, 0x6e, 0x76, + 0x73, 0x12, 0x56, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x40, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, + 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x5c, 0x0a, 0x07, 0x73, 0x65, 0x63, + 0x72, 0x65, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x42, 0x2e, 0x67, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, + 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, + 0x62, 0x52, 0x75, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, + 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x1a, 0x37, 0x0a, 0x09, 0x45, 0x6e, 0x76, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x1a, 0x38, 0x0a, 0x0a, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3a, 0x0a, 0x0c, 0x53, 0x65, + 0x63, 0x72, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x8f, 0x01, 0x0a, 0x0a, 0x54, 0x61, 0x73, 0x6b, 0x57, + 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, 0x2d, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, + 0x73, 0x69, 0x7a, 0x65, 0x12, 0x31, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x72, 0x75, 0x6e, 0x63, + 0x61, 0x74, 0x65, 0x5f, 0x74, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x72, + 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x54, 0x6f, 0x32, 0xa5, 0x06, 0x0a, 0x0d, 0x4a, 0x6f, 0x62, + 0x52, 0x75, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xbf, 0x01, 0x0a, 0x0b, 0x4a, + 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x34, 0x2e, 0x67, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, + 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, + 0x62, 0x52, 0x75, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x35, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, + 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x43, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3d, 0x22, + 0x38, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, + 0x2f, 0x6a, 0x6f, 0x62, 0x2f, 0x7b, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, + 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0xa7, 0x01, 0x0a, + 0x06, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x12, 0x2f, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x75, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x52, + 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3a, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x34, 0x12, 0x32, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x2f, 0x7b, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x7d, 0x2f, 0x72, 0x75, 0x6e, 0x12, 0xe5, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x65, 0x72, 0x4a, 0x6f, 0x62, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x39, 0x2e, 0x67, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, + 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4a, 0x6f, 0x62, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3a, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x65, 0x72, 0x4a, 0x6f, 0x62, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x5a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x54, 0x22, 0x4f, 0x2f, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x2f, 0x7b, 0x6a, 0x6f, 0x62, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0xbf, + 0x01, 0x0a, 0x11, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x53, 0x63, 0x68, 0x65, 0x64, + 0x75, 0x6c, 0x65, 0x72, 0x12, 0x3a, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x6f, + 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x3b, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, + 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x53, 0x63, 0x68, 0x65, + 0x64, 0x75, 0x6c, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x1a, 0x26, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, + 0x42, 0x8f, 0x01, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2e, 0x6f, 0x70, 0x74, 0x69, + 0x6d, 0x75, 0x73, 0x42, 0x0d, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x4d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x72, 0x50, 0x01, 0x5a, 0x1e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x67, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x92, 0x41, 0x3b, 0x12, 0x05, 0x32, 0x03, 0x30, 0x2e, 0x31, 0x1a, 0x0e, + 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x3a, 0x39, 0x31, 0x30, 0x30, 0x22, 0x04, + 0x2f, 0x61, 0x70, 0x69, 0x2a, 0x01, 0x01, 0x72, 0x19, 0x0a, 0x17, 0x4f, 0x70, 0x74, 0x69, 0x6d, + 0x75, 0x73, 0x20, 0x4a, 0x6f, 0x62, 0x20, 0x52, 0x75, 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_raystack_optimus_core_v1beta1_job_run_proto_rawDescOnce sync.Once + file_raystack_optimus_core_v1beta1_job_run_proto_rawDescData = file_raystack_optimus_core_v1beta1_job_run_proto_rawDesc +) + +func file_raystack_optimus_core_v1beta1_job_run_proto_rawDescGZIP() []byte { + file_raystack_optimus_core_v1beta1_job_run_proto_rawDescOnce.Do(func() { + file_raystack_optimus_core_v1beta1_job_run_proto_rawDescData = protoimpl.X.CompressGZIP(file_raystack_optimus_core_v1beta1_job_run_proto_rawDescData) + }) + return file_raystack_optimus_core_v1beta1_job_run_proto_rawDescData +} + +var file_raystack_optimus_core_v1beta1_job_run_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes = make([]protoimpl.MessageInfo, 14) +var file_raystack_optimus_core_v1beta1_job_run_proto_goTypes = []interface{}{ + (InstanceSpec_Type)(0), // 0: raystack.optimus.core.v1beta1.InstanceSpec.Type + (InstanceSpecData_Type)(0), // 1: raystack.optimus.core.v1beta1.InstanceSpecData.Type + (*UploadToSchedulerRequest)(nil), // 2: raystack.optimus.core.v1beta1.UploadToSchedulerRequest + (*UploadToSchedulerResponse)(nil), // 3: raystack.optimus.core.v1beta1.UploadToSchedulerResponse + (*RegisterJobEventRequest)(nil), // 4: raystack.optimus.core.v1beta1.RegisterJobEventRequest + (*RegisterJobEventResponse)(nil), // 5: raystack.optimus.core.v1beta1.RegisterJobEventResponse + (*JobRunInputRequest)(nil), // 6: raystack.optimus.core.v1beta1.JobRunInputRequest + (*JobRunRequest)(nil), // 7: raystack.optimus.core.v1beta1.JobRunRequest + (*JobRunResponse)(nil), // 8: raystack.optimus.core.v1beta1.JobRunResponse + (*InstanceSpec)(nil), // 9: raystack.optimus.core.v1beta1.InstanceSpec + (*InstanceSpecData)(nil), // 10: raystack.optimus.core.v1beta1.InstanceSpecData + (*JobRunInputResponse)(nil), // 11: raystack.optimus.core.v1beta1.JobRunInputResponse + (*TaskWindow)(nil), // 12: raystack.optimus.core.v1beta1.TaskWindow + nil, // 13: raystack.optimus.core.v1beta1.JobRunInputResponse.EnvsEntry + nil, // 14: raystack.optimus.core.v1beta1.JobRunInputResponse.FilesEntry + nil, // 15: raystack.optimus.core.v1beta1.JobRunInputResponse.SecretsEntry + (*JobEvent)(nil), // 16: raystack.optimus.core.v1beta1.JobEvent + (*timestamppb.Timestamp)(nil), // 17: google.protobuf.Timestamp + (*JobRun)(nil), // 18: raystack.optimus.core.v1beta1.JobRun + (*durationpb.Duration)(nil), // 19: google.protobuf.Duration +} +var file_raystack_optimus_core_v1beta1_job_run_proto_depIdxs = []int32{ + 16, // 0: raystack.optimus.core.v1beta1.RegisterJobEventRequest.event:type_name -> raystack.optimus.core.v1beta1.JobEvent + 17, // 1: raystack.optimus.core.v1beta1.JobRunInputRequest.scheduled_at:type_name -> google.protobuf.Timestamp + 0, // 2: raystack.optimus.core.v1beta1.JobRunInputRequest.instance_type:type_name -> raystack.optimus.core.v1beta1.InstanceSpec.Type + 17, // 3: raystack.optimus.core.v1beta1.JobRunRequest.start_date:type_name -> google.protobuf.Timestamp + 17, // 4: raystack.optimus.core.v1beta1.JobRunRequest.end_date:type_name -> google.protobuf.Timestamp + 18, // 5: raystack.optimus.core.v1beta1.JobRunResponse.job_runs:type_name -> raystack.optimus.core.v1beta1.JobRun + 10, // 6: raystack.optimus.core.v1beta1.InstanceSpec.data:type_name -> raystack.optimus.core.v1beta1.InstanceSpecData + 17, // 7: raystack.optimus.core.v1beta1.InstanceSpec.executed_at:type_name -> google.protobuf.Timestamp + 0, // 8: raystack.optimus.core.v1beta1.InstanceSpec.type:type_name -> raystack.optimus.core.v1beta1.InstanceSpec.Type + 1, // 9: raystack.optimus.core.v1beta1.InstanceSpecData.type:type_name -> raystack.optimus.core.v1beta1.InstanceSpecData.Type + 13, // 10: raystack.optimus.core.v1beta1.JobRunInputResponse.envs:type_name -> raystack.optimus.core.v1beta1.JobRunInputResponse.EnvsEntry + 14, // 11: raystack.optimus.core.v1beta1.JobRunInputResponse.files:type_name -> raystack.optimus.core.v1beta1.JobRunInputResponse.FilesEntry + 15, // 12: raystack.optimus.core.v1beta1.JobRunInputResponse.secrets:type_name -> raystack.optimus.core.v1beta1.JobRunInputResponse.SecretsEntry + 19, // 13: raystack.optimus.core.v1beta1.TaskWindow.size:type_name -> google.protobuf.Duration + 19, // 14: raystack.optimus.core.v1beta1.TaskWindow.offset:type_name -> google.protobuf.Duration + 6, // 15: raystack.optimus.core.v1beta1.JobRunService.JobRunInput:input_type -> raystack.optimus.core.v1beta1.JobRunInputRequest + 7, // 16: raystack.optimus.core.v1beta1.JobRunService.JobRun:input_type -> raystack.optimus.core.v1beta1.JobRunRequest + 4, // 17: raystack.optimus.core.v1beta1.JobRunService.RegisterJobEvent:input_type -> raystack.optimus.core.v1beta1.RegisterJobEventRequest + 2, // 18: raystack.optimus.core.v1beta1.JobRunService.UploadToScheduler:input_type -> raystack.optimus.core.v1beta1.UploadToSchedulerRequest + 11, // 19: raystack.optimus.core.v1beta1.JobRunService.JobRunInput:output_type -> raystack.optimus.core.v1beta1.JobRunInputResponse + 8, // 20: raystack.optimus.core.v1beta1.JobRunService.JobRun:output_type -> raystack.optimus.core.v1beta1.JobRunResponse + 5, // 21: raystack.optimus.core.v1beta1.JobRunService.RegisterJobEvent:output_type -> raystack.optimus.core.v1beta1.RegisterJobEventResponse + 3, // 22: raystack.optimus.core.v1beta1.JobRunService.UploadToScheduler:output_type -> raystack.optimus.core.v1beta1.UploadToSchedulerResponse + 19, // [19:23] is the sub-list for method output_type + 15, // [15:19] is the sub-list for method input_type + 15, // [15:15] is the sub-list for extension type_name + 15, // [15:15] is the sub-list for extension extendee + 0, // [0:15] is the sub-list for field type_name +} + +func init() { file_raystack_optimus_core_v1beta1_job_run_proto_init() } +func file_raystack_optimus_core_v1beta1_job_run_proto_init() { + if File_raystack_optimus_core_v1beta1_job_run_proto != nil { + return + } + file_raystack_optimus_core_v1beta1_job_spec_proto_init() + if !protoimpl.UnsafeEnabled { + file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UploadToSchedulerRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UploadToSchedulerResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterJobEventRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterJobEventResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*JobRunInputRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*JobRunRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*JobRunResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InstanceSpec); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InstanceSpecData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*JobRunInputResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TaskWindow); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes[0].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_raystack_optimus_core_v1beta1_job_run_proto_rawDesc, + NumEnums: 2, + NumMessages: 14, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_raystack_optimus_core_v1beta1_job_run_proto_goTypes, + DependencyIndexes: file_raystack_optimus_core_v1beta1_job_run_proto_depIdxs, + EnumInfos: file_raystack_optimus_core_v1beta1_job_run_proto_enumTypes, + MessageInfos: file_raystack_optimus_core_v1beta1_job_run_proto_msgTypes, + }.Build() + File_raystack_optimus_core_v1beta1_job_run_proto = out.File + file_raystack_optimus_core_v1beta1_job_run_proto_rawDesc = nil + file_raystack_optimus_core_v1beta1_job_run_proto_goTypes = nil + file_raystack_optimus_core_v1beta1_job_run_proto_depIdxs = nil +} diff --git a/protos/odpf/optimus/core/v1beta1/job_run.pb.gw.go b/protos/raystack/optimus/core/v1beta1/job_run.pb.gw.go similarity index 94% rename from protos/odpf/optimus/core/v1beta1/job_run.pb.gw.go rename to protos/raystack/optimus/core/v1beta1/job_run.pb.gw.go index e862b2b570..51c7c5ab20 100644 --- a/protos/odpf/optimus/core/v1beta1/job_run.pb.gw.go +++ b/protos/raystack/optimus/core/v1beta1/job_run.pb.gw.go @@ -1,5 +1,5 @@ // Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. -// source: odpf/optimus/core/v1beta1/job_run.proto +// source: raystack/optimus/core/v1beta1/job_run.proto /* Package optimus is a reverse proxy. @@ -397,7 +397,7 @@ func RegisterJobRunServiceHandlerServer(ctx context.Context, mux *runtime.ServeM var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobRunService/JobRunInput", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/job/{job_name}/run_input")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobRunService/JobRunInput", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/job/{job_name}/run_input")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -420,7 +420,7 @@ func RegisterJobRunServiceHandlerServer(ctx context.Context, mux *runtime.ServeM var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobRunService/JobRun", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/job/{job_name}/run")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobRunService/JobRun", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/job/{job_name}/run")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -443,7 +443,7 @@ func RegisterJobRunServiceHandlerServer(ctx context.Context, mux *runtime.ServeM var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobRunService/RegisterJobEvent", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job/{job_name}/event")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobRunService/RegisterJobEvent", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job/{job_name}/event")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -466,7 +466,7 @@ func RegisterJobRunServiceHandlerServer(ctx context.Context, mux *runtime.ServeM var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobRunService/UploadToScheduler", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/upload")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobRunService/UploadToScheduler", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/upload")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -528,7 +528,7 @@ func RegisterJobRunServiceHandlerClient(ctx context.Context, mux *runtime.ServeM ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobRunService/JobRunInput", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/job/{job_name}/run_input")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobRunService/JobRunInput", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/job/{job_name}/run_input")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -548,7 +548,7 @@ func RegisterJobRunServiceHandlerClient(ctx context.Context, mux *runtime.ServeM ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobRunService/JobRun", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/job/{job_name}/run")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobRunService/JobRun", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/job/{job_name}/run")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -568,7 +568,7 @@ func RegisterJobRunServiceHandlerClient(ctx context.Context, mux *runtime.ServeM ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobRunService/RegisterJobEvent", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job/{job_name}/event")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobRunService/RegisterJobEvent", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job/{job_name}/event")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -588,7 +588,7 @@ func RegisterJobRunServiceHandlerClient(ctx context.Context, mux *runtime.ServeM ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobRunService/UploadToScheduler", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/upload")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobRunService/UploadToScheduler", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/upload")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return diff --git a/protos/odpf/optimus/core/v1beta1/job_run.swagger.json b/protos/raystack/optimus/core/v1beta1/job_run.swagger.json similarity index 94% rename from protos/odpf/optimus/core/v1beta1/job_run.swagger.json rename to protos/raystack/optimus/core/v1beta1/job_run.swagger.json index 0205dbf7ad..124f1198d4 100644 --- a/protos/odpf/optimus/core/v1beta1/job_run.swagger.json +++ b/protos/raystack/optimus/core/v1beta1/job_run.swagger.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "title": "odpf/optimus/core/v1beta1/job_run.proto", + "title": "raystack/optimus/core/v1beta1/job_run.proto", "version": "0.1" }, "tags": [ @@ -11,15 +11,9 @@ ], "host": "127.0.0.1:9100", "basePath": "/api", - "schemes": [ - "http" - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], + "schemes": ["http"], + "consumes": ["application/json"], + "produces": ["application/json"], "paths": { "/v1beta1/project/{projectName}/job/{jobName}/run": { "get": { @@ -77,9 +71,7 @@ "collectionFormat": "multi" } ], - "tags": [ - "JobRunService" - ] + "tags": ["JobRunService"] } }, "/v1beta1/project/{projectName}/job/{jobName}/run_input": { @@ -138,9 +130,7 @@ } } ], - "tags": [ - "JobRunService" - ] + "tags": ["JobRunService"] } }, "/v1beta1/project/{projectName}/namespace/{namespaceName}/job/{jobName}/event": { @@ -194,9 +184,7 @@ } } ], - "tags": [ - "JobRunService" - ] + "tags": ["JobRunService"] } }, "/v1beta1/project/{projectName}/upload": { @@ -238,9 +226,7 @@ } } ], - "tags": [ - "JobRunService" - ] + "tags": ["JobRunService"] } } }, @@ -259,9 +245,7 @@ }, "protobufNullValue": { "type": "string", - "enum": [ - "NULL_VALUE" - ], + "enum": ["NULL_VALUE"], "default": "NULL_VALUE", "description": "`NullValue` is a singleton enumeration to represent the null value for the\n`Value` type union.\n\n The JSON representation for `NullValue` is JSON `null`.\n\n - NULL_VALUE: Null value." }, @@ -285,11 +269,7 @@ }, "v1beta1InstanceSpecType": { "type": "string", - "enum": [ - "TYPE_UNSPECIFIED", - "TYPE_TASK", - "TYPE_HOOK" - ], + "enum": ["TYPE_UNSPECIFIED", "TYPE_TASK", "TYPE_HOOK"], "default": "TYPE_UNSPECIFIED" }, "v1beta1JobEvent": { diff --git a/protos/odpf/optimus/core/v1beta1/job_run_grpc.pb.go b/protos/raystack/optimus/core/v1beta1/job_run_grpc.pb.go similarity index 90% rename from protos/odpf/optimus/core/v1beta1/job_run_grpc.pb.go rename to protos/raystack/optimus/core/v1beta1/job_run_grpc.pb.go index f2f716fce0..fca6b5646f 100644 --- a/protos/odpf/optimus/core/v1beta1/job_run_grpc.pb.go +++ b/protos/raystack/optimus/core/v1beta1/job_run_grpc.pb.go @@ -2,7 +2,7 @@ // versions: // - protoc-gen-go-grpc v1.2.0 // - protoc (unknown) -// source: odpf/optimus/core/v1beta1/job_run.proto +// source: raystack/optimus/core/v1beta1/job_run.proto package optimus @@ -42,7 +42,7 @@ func NewJobRunServiceClient(cc grpc.ClientConnInterface) JobRunServiceClient { func (c *jobRunServiceClient) JobRunInput(ctx context.Context, in *JobRunInputRequest, opts ...grpc.CallOption) (*JobRunInputResponse, error) { out := new(JobRunInputResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.JobRunService/JobRunInput", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.JobRunService/JobRunInput", in, out, opts...) if err != nil { return nil, err } @@ -51,7 +51,7 @@ func (c *jobRunServiceClient) JobRunInput(ctx context.Context, in *JobRunInputRe func (c *jobRunServiceClient) JobRun(ctx context.Context, in *JobRunRequest, opts ...grpc.CallOption) (*JobRunResponse, error) { out := new(JobRunResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.JobRunService/JobRun", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.JobRunService/JobRun", in, out, opts...) if err != nil { return nil, err } @@ -60,7 +60,7 @@ func (c *jobRunServiceClient) JobRun(ctx context.Context, in *JobRunRequest, opt func (c *jobRunServiceClient) RegisterJobEvent(ctx context.Context, in *RegisterJobEventRequest, opts ...grpc.CallOption) (*RegisterJobEventResponse, error) { out := new(RegisterJobEventResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.JobRunService/RegisterJobEvent", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.JobRunService/RegisterJobEvent", in, out, opts...) if err != nil { return nil, err } @@ -69,7 +69,7 @@ func (c *jobRunServiceClient) RegisterJobEvent(ctx context.Context, in *Register func (c *jobRunServiceClient) UploadToScheduler(ctx context.Context, in *UploadToSchedulerRequest, opts ...grpc.CallOption) (*UploadToSchedulerResponse, error) { out := new(UploadToSchedulerResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.JobRunService/UploadToScheduler", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.JobRunService/UploadToScheduler", in, out, opts...) if err != nil { return nil, err } @@ -130,7 +130,7 @@ func _JobRunService_JobRunInput_Handler(srv interface{}, ctx context.Context, de } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.JobRunService/JobRunInput", + FullMethod: "/raystack.optimus.core.v1beta1.JobRunService/JobRunInput", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(JobRunServiceServer).JobRunInput(ctx, req.(*JobRunInputRequest)) @@ -148,7 +148,7 @@ func _JobRunService_JobRun_Handler(srv interface{}, ctx context.Context, dec fun } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.JobRunService/JobRun", + FullMethod: "/raystack.optimus.core.v1beta1.JobRunService/JobRun", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(JobRunServiceServer).JobRun(ctx, req.(*JobRunRequest)) @@ -166,7 +166,7 @@ func _JobRunService_RegisterJobEvent_Handler(srv interface{}, ctx context.Contex } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.JobRunService/RegisterJobEvent", + FullMethod: "/raystack.optimus.core.v1beta1.JobRunService/RegisterJobEvent", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(JobRunServiceServer).RegisterJobEvent(ctx, req.(*RegisterJobEventRequest)) @@ -184,7 +184,7 @@ func _JobRunService_UploadToScheduler_Handler(srv interface{}, ctx context.Conte } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.JobRunService/UploadToScheduler", + FullMethod: "/raystack.optimus.core.v1beta1.JobRunService/UploadToScheduler", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(JobRunServiceServer).UploadToScheduler(ctx, req.(*UploadToSchedulerRequest)) @@ -196,7 +196,7 @@ func _JobRunService_UploadToScheduler_Handler(srv interface{}, ctx context.Conte // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var JobRunService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "odpf.optimus.core.v1beta1.JobRunService", + ServiceName: "raystack.optimus.core.v1beta1.JobRunService", HandlerType: (*JobRunServiceServer)(nil), Methods: []grpc.MethodDesc{ { @@ -217,5 +217,5 @@ var JobRunService_ServiceDesc = grpc.ServiceDesc{ }, }, Streams: []grpc.StreamDesc{}, - Metadata: "odpf/optimus/core/v1beta1/job_run.proto", + Metadata: "raystack/optimus/core/v1beta1/job_run.proto", } diff --git a/protos/odpf/optimus/core/v1beta1/job_spec.pb.go b/protos/raystack/optimus/core/v1beta1/job_spec.pb.go similarity index 51% rename from protos/odpf/optimus/core/v1beta1/job_spec.pb.go rename to protos/raystack/optimus/core/v1beta1/job_spec.pb.go index 4d73531d92..8ea2b1ee54 100644 --- a/protos/odpf/optimus/core/v1beta1/job_spec.pb.go +++ b/protos/raystack/optimus/core/v1beta1/job_spec.pb.go @@ -2,7 +2,7 @@ // versions: // protoc-gen-go v1.28.0 // protoc (unknown) -// source: odpf/optimus/core/v1beta1/job_spec.proto +// source: raystack/optimus/core/v1beta1/job_spec.proto package optimus @@ -25,6 +25,55 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type JobState int32 + +const ( + JobState_JOB_STATE_UNSPECIFIED JobState = 0 + JobState_JOB_STATE_ENABLED JobState = 1 + JobState_JOB_STATE_DISABLED JobState = 2 +) + +// Enum value maps for JobState. +var ( + JobState_name = map[int32]string{ + 0: "JOB_STATE_UNSPECIFIED", + 1: "JOB_STATE_ENABLED", + 2: "JOB_STATE_DISABLED", + } + JobState_value = map[string]int32{ + "JOB_STATE_UNSPECIFIED": 0, + "JOB_STATE_ENABLED": 1, + "JOB_STATE_DISABLED": 2, + } +) + +func (x JobState) Enum() *JobState { + p := new(JobState) + *p = x + return p +} + +func (x JobState) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (JobState) Descriptor() protoreflect.EnumDescriptor { + return file_raystack_optimus_core_v1beta1_job_spec_proto_enumTypes[0].Descriptor() +} + +func (JobState) Type() protoreflect.EnumType { + return &file_raystack_optimus_core_v1beta1_job_spec_proto_enumTypes[0] +} + +func (x JobState) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use JobState.Descriptor instead. +func (JobState) EnumDescriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{0} +} + type JobEvent_Type int32 const ( @@ -97,11 +146,11 @@ func (x JobEvent_Type) String() string { } func (JobEvent_Type) Descriptor() protoreflect.EnumDescriptor { - return file_odpf_optimus_core_v1beta1_job_spec_proto_enumTypes[0].Descriptor() + return file_raystack_optimus_core_v1beta1_job_spec_proto_enumTypes[1].Descriptor() } func (JobEvent_Type) Type() protoreflect.EnumType { - return &file_odpf_optimus_core_v1beta1_job_spec_proto_enumTypes[0] + return &file_raystack_optimus_core_v1beta1_job_spec_proto_enumTypes[1] } func (x JobEvent_Type) Number() protoreflect.EnumNumber { @@ -110,7 +159,7 @@ func (x JobEvent_Type) Number() protoreflect.EnumNumber { // Deprecated: Use JobEvent_Type.Descriptor instead. func (JobEvent_Type) EnumDescriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{26, 0} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{28, 0} } type DeployJobSpecificationRequest struct { @@ -126,7 +175,7 @@ type DeployJobSpecificationRequest struct { func (x *DeployJobSpecificationRequest) Reset() { *x = DeployJobSpecificationRequest{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[0] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -139,7 +188,7 @@ func (x *DeployJobSpecificationRequest) String() string { func (*DeployJobSpecificationRequest) ProtoMessage() {} func (x *DeployJobSpecificationRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[0] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -152,7 +201,7 @@ func (x *DeployJobSpecificationRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeployJobSpecificationRequest.ProtoReflect.Descriptor instead. func (*DeployJobSpecificationRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{0} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{0} } func (x *DeployJobSpecificationRequest) GetProjectName() string { @@ -190,7 +239,7 @@ type DeployJobSpecificationResponse struct { func (x *DeployJobSpecificationResponse) Reset() { *x = DeployJobSpecificationResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[1] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -203,7 +252,7 @@ func (x *DeployJobSpecificationResponse) String() string { func (*DeployJobSpecificationResponse) ProtoMessage() {} func (x *DeployJobSpecificationResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[1] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -216,7 +265,7 @@ func (x *DeployJobSpecificationResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeployJobSpecificationResponse.ProtoReflect.Descriptor instead. func (*DeployJobSpecificationResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{1} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{1} } func (x *DeployJobSpecificationResponse) GetDeploymentId() string { @@ -246,7 +295,7 @@ type AddJobSpecificationsRequest struct { func (x *AddJobSpecificationsRequest) Reset() { *x = AddJobSpecificationsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[2] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -259,7 +308,7 @@ func (x *AddJobSpecificationsRequest) String() string { func (*AddJobSpecificationsRequest) ProtoMessage() {} func (x *AddJobSpecificationsRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[2] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -272,7 +321,7 @@ func (x *AddJobSpecificationsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AddJobSpecificationsRequest.ProtoReflect.Descriptor instead. func (*AddJobSpecificationsRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{2} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{2} } func (x *AddJobSpecificationsRequest) GetProjectName() string { @@ -307,7 +356,7 @@ type AddJobSpecificationsResponse struct { func (x *AddJobSpecificationsResponse) Reset() { *x = AddJobSpecificationsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[3] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -320,7 +369,7 @@ func (x *AddJobSpecificationsResponse) String() string { func (*AddJobSpecificationsResponse) ProtoMessage() {} func (x *AddJobSpecificationsResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[3] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -333,7 +382,7 @@ func (x *AddJobSpecificationsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AddJobSpecificationsResponse.ProtoReflect.Descriptor instead. func (*AddJobSpecificationsResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{3} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{3} } func (x *AddJobSpecificationsResponse) GetLog() string { @@ -356,7 +405,7 @@ type UpdateJobSpecificationsRequest struct { func (x *UpdateJobSpecificationsRequest) Reset() { *x = UpdateJobSpecificationsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[4] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -369,7 +418,7 @@ func (x *UpdateJobSpecificationsRequest) String() string { func (*UpdateJobSpecificationsRequest) ProtoMessage() {} func (x *UpdateJobSpecificationsRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[4] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -382,7 +431,7 @@ func (x *UpdateJobSpecificationsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateJobSpecificationsRequest.ProtoReflect.Descriptor instead. func (*UpdateJobSpecificationsRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{4} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{4} } func (x *UpdateJobSpecificationsRequest) GetProjectName() string { @@ -417,7 +466,7 @@ type UpdateJobSpecificationsResponse struct { func (x *UpdateJobSpecificationsResponse) Reset() { *x = UpdateJobSpecificationsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[5] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -430,7 +479,7 @@ func (x *UpdateJobSpecificationsResponse) String() string { func (*UpdateJobSpecificationsResponse) ProtoMessage() {} func (x *UpdateJobSpecificationsResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[5] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -443,7 +492,7 @@ func (x *UpdateJobSpecificationsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateJobSpecificationsResponse.ProtoReflect.Descriptor instead. func (*UpdateJobSpecificationsResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{5} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{5} } func (x *UpdateJobSpecificationsResponse) GetLog() string { @@ -468,7 +517,7 @@ type JobInspectRequest struct { func (x *JobInspectRequest) Reset() { *x = JobInspectRequest{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[6] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -481,7 +530,7 @@ func (x *JobInspectRequest) String() string { func (*JobInspectRequest) ProtoMessage() {} func (x *JobInspectRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[6] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -494,7 +543,7 @@ func (x *JobInspectRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use JobInspectRequest.ProtoReflect.Descriptor instead. func (*JobInspectRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{6} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{6} } func (x *JobInspectRequest) GetProjectName() string { @@ -544,7 +593,7 @@ type JobRun struct { func (x *JobRun) Reset() { *x = JobRun{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[7] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -557,7 +606,7 @@ func (x *JobRun) String() string { func (*JobRun) ProtoMessage() {} func (x *JobRun) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[7] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -570,7 +619,7 @@ func (x *JobRun) ProtoReflect() protoreflect.Message { // Deprecated: Use JobRun.ProtoReflect.Descriptor instead. func (*JobRun) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{7} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{7} } func (x *JobRun) GetState() string { @@ -600,7 +649,7 @@ type JobInspectResponse struct { func (x *JobInspectResponse) Reset() { *x = JobInspectResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[8] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -613,7 +662,7 @@ func (x *JobInspectResponse) String() string { func (*JobInspectResponse) ProtoMessage() {} func (x *JobInspectResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[8] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -626,7 +675,7 @@ func (x *JobInspectResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use JobInspectResponse.ProtoReflect.Descriptor instead. func (*JobInspectResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{8} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{8} } func (x *JobInspectResponse) GetBasicInfo() *JobInspectResponse_BasicInfoSection { @@ -663,7 +712,7 @@ type CreateJobSpecificationRequest struct { func (x *CreateJobSpecificationRequest) Reset() { *x = CreateJobSpecificationRequest{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[9] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -676,7 +725,7 @@ func (x *CreateJobSpecificationRequest) String() string { func (*CreateJobSpecificationRequest) ProtoMessage() {} func (x *CreateJobSpecificationRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[9] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -689,7 +738,7 @@ func (x *CreateJobSpecificationRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateJobSpecificationRequest.ProtoReflect.Descriptor instead. func (*CreateJobSpecificationRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{9} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{9} } func (x *CreateJobSpecificationRequest) GetProjectName() string { @@ -725,7 +774,7 @@ type CreateJobSpecificationResponse struct { func (x *CreateJobSpecificationResponse) Reset() { *x = CreateJobSpecificationResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[10] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -738,7 +787,7 @@ func (x *CreateJobSpecificationResponse) String() string { func (*CreateJobSpecificationResponse) ProtoMessage() {} func (x *CreateJobSpecificationResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[10] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -751,7 +800,7 @@ func (x *CreateJobSpecificationResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateJobSpecificationResponse.ProtoReflect.Descriptor instead. func (*CreateJobSpecificationResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{10} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{10} } func (x *CreateJobSpecificationResponse) GetSuccess() bool { @@ -781,7 +830,7 @@ type GetJobSpecificationRequest struct { func (x *GetJobSpecificationRequest) Reset() { *x = GetJobSpecificationRequest{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[11] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -794,7 +843,7 @@ func (x *GetJobSpecificationRequest) String() string { func (*GetJobSpecificationRequest) ProtoMessage() {} func (x *GetJobSpecificationRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[11] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -807,7 +856,7 @@ func (x *GetJobSpecificationRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetJobSpecificationRequest.ProtoReflect.Descriptor instead. func (*GetJobSpecificationRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{11} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{11} } func (x *GetJobSpecificationRequest) GetProjectName() string { @@ -842,7 +891,7 @@ type GetJobSpecificationResponse struct { func (x *GetJobSpecificationResponse) Reset() { *x = GetJobSpecificationResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[12] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -855,7 +904,7 @@ func (x *GetJobSpecificationResponse) String() string { func (*GetJobSpecificationResponse) ProtoMessage() {} func (x *GetJobSpecificationResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[12] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -868,7 +917,7 @@ func (x *GetJobSpecificationResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetJobSpecificationResponse.ProtoReflect.Descriptor instead. func (*GetJobSpecificationResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{12} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{12} } func (x *GetJobSpecificationResponse) GetSpec() *JobSpecification { @@ -893,7 +942,7 @@ type DeleteJobSpecificationRequest struct { func (x *DeleteJobSpecificationRequest) Reset() { *x = DeleteJobSpecificationRequest{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[13] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -906,7 +955,7 @@ func (x *DeleteJobSpecificationRequest) String() string { func (*DeleteJobSpecificationRequest) ProtoMessage() {} func (x *DeleteJobSpecificationRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[13] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -919,7 +968,7 @@ func (x *DeleteJobSpecificationRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteJobSpecificationRequest.ProtoReflect.Descriptor instead. func (*DeleteJobSpecificationRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{13} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{13} } func (x *DeleteJobSpecificationRequest) GetProjectName() string { @@ -969,7 +1018,7 @@ type DeleteJobSpecificationResponse struct { func (x *DeleteJobSpecificationResponse) Reset() { *x = DeleteJobSpecificationResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[14] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -982,7 +1031,7 @@ func (x *DeleteJobSpecificationResponse) String() string { func (*DeleteJobSpecificationResponse) ProtoMessage() {} func (x *DeleteJobSpecificationResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[14] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -995,7 +1044,7 @@ func (x *DeleteJobSpecificationResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteJobSpecificationResponse.ProtoReflect.Descriptor instead. func (*DeleteJobSpecificationResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{14} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{14} } func (x *DeleteJobSpecificationResponse) GetSuccess() bool { @@ -1012,6 +1061,115 @@ func (x *DeleteJobSpecificationResponse) GetMessage() string { return "" } +type ChangeJobNamespaceRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + NamespaceName string `protobuf:"bytes,2,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` + JobName string `protobuf:"bytes,3,opt,name=job_name,json=jobName,proto3" json:"job_name,omitempty"` + NewNamespaceName string `protobuf:"bytes,4,opt,name=new_namespace_name,json=newNamespaceName,proto3" json:"new_namespace_name,omitempty"` +} + +func (x *ChangeJobNamespaceRequest) Reset() { + *x = ChangeJobNamespaceRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ChangeJobNamespaceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ChangeJobNamespaceRequest) ProtoMessage() {} + +func (x *ChangeJobNamespaceRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ChangeJobNamespaceRequest.ProtoReflect.Descriptor instead. +func (*ChangeJobNamespaceRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{15} +} + +func (x *ChangeJobNamespaceRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *ChangeJobNamespaceRequest) GetNamespaceName() string { + if x != nil { + return x.NamespaceName + } + return "" +} + +func (x *ChangeJobNamespaceRequest) GetJobName() string { + if x != nil { + return x.JobName + } + return "" +} + +func (x *ChangeJobNamespaceRequest) GetNewNamespaceName() string { + if x != nil { + return x.NewNamespaceName + } + return "" +} + +type ChangeJobNamespaceResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ChangeJobNamespaceResponse) Reset() { + *x = ChangeJobNamespaceResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ChangeJobNamespaceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ChangeJobNamespaceResponse) ProtoMessage() {} + +func (x *ChangeJobNamespaceResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ChangeJobNamespaceResponse.ProtoReflect.Descriptor instead. +func (*ChangeJobNamespaceResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{16} +} + type ListJobSpecificationRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1024,7 +1182,7 @@ type ListJobSpecificationRequest struct { func (x *ListJobSpecificationRequest) Reset() { *x = ListJobSpecificationRequest{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[15] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1037,7 +1195,7 @@ func (x *ListJobSpecificationRequest) String() string { func (*ListJobSpecificationRequest) ProtoMessage() {} func (x *ListJobSpecificationRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[15] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1050,7 +1208,7 @@ func (x *ListJobSpecificationRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListJobSpecificationRequest.ProtoReflect.Descriptor instead. func (*ListJobSpecificationRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{15} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{17} } func (x *ListJobSpecificationRequest) GetProjectName() string { @@ -1078,7 +1236,7 @@ type ListJobSpecificationResponse struct { func (x *ListJobSpecificationResponse) Reset() { *x = ListJobSpecificationResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[16] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1091,7 +1249,7 @@ func (x *ListJobSpecificationResponse) String() string { func (*ListJobSpecificationResponse) ProtoMessage() {} func (x *ListJobSpecificationResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[16] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1104,7 +1262,7 @@ func (x *ListJobSpecificationResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListJobSpecificationResponse.ProtoReflect.Descriptor instead. func (*ListJobSpecificationResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{16} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{18} } func (x *ListJobSpecificationResponse) GetJobs() []*JobSpecification { @@ -1127,7 +1285,7 @@ type CheckJobSpecificationRequest struct { func (x *CheckJobSpecificationRequest) Reset() { *x = CheckJobSpecificationRequest{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[17] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1140,7 +1298,7 @@ func (x *CheckJobSpecificationRequest) String() string { func (*CheckJobSpecificationRequest) ProtoMessage() {} func (x *CheckJobSpecificationRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[17] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1153,7 +1311,7 @@ func (x *CheckJobSpecificationRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckJobSpecificationRequest.ProtoReflect.Descriptor instead. func (*CheckJobSpecificationRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{17} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{19} } func (x *CheckJobSpecificationRequest) GetProjectName() string { @@ -1188,7 +1346,7 @@ type CheckJobSpecificationResponse struct { func (x *CheckJobSpecificationResponse) Reset() { *x = CheckJobSpecificationResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[18] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1201,7 +1359,7 @@ func (x *CheckJobSpecificationResponse) String() string { func (*CheckJobSpecificationResponse) ProtoMessage() {} func (x *CheckJobSpecificationResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[18] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1214,7 +1372,7 @@ func (x *CheckJobSpecificationResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckJobSpecificationResponse.ProtoReflect.Descriptor instead. func (*CheckJobSpecificationResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{18} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{20} } func (x *CheckJobSpecificationResponse) GetSuccess() bool { @@ -1237,7 +1395,7 @@ type CheckJobSpecificationsRequest struct { func (x *CheckJobSpecificationsRequest) Reset() { *x = CheckJobSpecificationsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[19] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1250,7 +1408,7 @@ func (x *CheckJobSpecificationsRequest) String() string { func (*CheckJobSpecificationsRequest) ProtoMessage() {} func (x *CheckJobSpecificationsRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[19] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1263,7 +1421,7 @@ func (x *CheckJobSpecificationsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckJobSpecificationsRequest.ProtoReflect.Descriptor instead. func (*CheckJobSpecificationsRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{19} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{21} } func (x *CheckJobSpecificationsRequest) GetProjectName() string { @@ -1298,7 +1456,7 @@ type CheckJobSpecificationsResponse struct { func (x *CheckJobSpecificationsResponse) Reset() { *x = CheckJobSpecificationsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[20] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1311,7 +1469,7 @@ func (x *CheckJobSpecificationsResponse) String() string { func (*CheckJobSpecificationsResponse) ProtoMessage() {} func (x *CheckJobSpecificationsResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[20] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1324,7 +1482,7 @@ func (x *CheckJobSpecificationsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckJobSpecificationsResponse.ProtoReflect.Descriptor instead. func (*CheckJobSpecificationsResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{20} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{22} } func (x *CheckJobSpecificationsResponse) GetLogStatus() *Log { @@ -1339,14 +1497,15 @@ type JobSpecification struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Version int32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - Owner string `protobuf:"bytes,3,opt,name=owner,proto3" json:"owner,omitempty"` - StartDate string `protobuf:"bytes,4,opt,name=start_date,json=startDate,proto3" json:"start_date,omitempty"` - EndDate string `protobuf:"bytes,5,opt,name=end_date,json=endDate,proto3" json:"end_date,omitempty"` // optional - Interval string `protobuf:"bytes,6,opt,name=interval,proto3" json:"interval,omitempty"` - DependsOnPast bool `protobuf:"varint,7,opt,name=depends_on_past,json=dependsOnPast,proto3" json:"depends_on_past,omitempty"` // should only execute today if yesterday was completed with success? - CatchUp bool `protobuf:"varint,8,opt,name=catch_up,json=catchUp,proto3" json:"catch_up,omitempty"` // should backfill till today? + Version int32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Owner string `protobuf:"bytes,3,opt,name=owner,proto3" json:"owner,omitempty"` + StartDate string `protobuf:"bytes,4,opt,name=start_date,json=startDate,proto3" json:"start_date,omitempty"` + EndDate string `protobuf:"bytes,5,opt,name=end_date,json=endDate,proto3" json:"end_date,omitempty"` // optional + Interval string `protobuf:"bytes,6,opt,name=interval,proto3" json:"interval,omitempty"` + DependsOnPast bool `protobuf:"varint,7,opt,name=depends_on_past,json=dependsOnPast,proto3" json:"depends_on_past,omitempty"` // should only execute today if yesterday was completed with success? + // Deprecated: Do not use. + CatchUp bool `protobuf:"varint,8,opt,name=catch_up,json=catchUp,proto3" json:"catch_up,omitempty"` // should backfill till today? TaskName string `protobuf:"bytes,9,opt,name=task_name,json=taskName,proto3" json:"task_name,omitempty"` Config []*JobConfigItem `protobuf:"bytes,10,rep,name=config,proto3" json:"config,omitempty"` WindowSize string `protobuf:"bytes,11,opt,name=window_size,json=windowSize,proto3" json:"window_size,omitempty"` @@ -1366,7 +1525,7 @@ type JobSpecification struct { func (x *JobSpecification) Reset() { *x = JobSpecification{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[21] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1379,7 +1538,7 @@ func (x *JobSpecification) String() string { func (*JobSpecification) ProtoMessage() {} func (x *JobSpecification) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[21] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1392,7 +1551,7 @@ func (x *JobSpecification) ProtoReflect() protoreflect.Message { // Deprecated: Use JobSpecification.ProtoReflect.Descriptor instead. func (*JobSpecification) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{21} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{23} } func (x *JobSpecification) GetVersion() int32 { @@ -1444,6 +1603,7 @@ func (x *JobSpecification) GetDependsOnPast() bool { return false } +// Deprecated: Do not use. func (x *JobSpecification) GetCatchUp() bool { if x != nil { return x.CatchUp @@ -1562,7 +1722,7 @@ type JobDependency struct { func (x *JobDependency) Reset() { *x = JobDependency{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[22] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1575,7 +1735,7 @@ func (x *JobDependency) String() string { func (*JobDependency) ProtoMessage() {} func (x *JobDependency) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[22] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1588,7 +1748,7 @@ func (x *JobDependency) ProtoReflect() protoreflect.Message { // Deprecated: Use JobDependency.ProtoReflect.Descriptor instead. func (*JobDependency) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{22} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{24} } func (x *JobDependency) GetName() string { @@ -1626,7 +1786,7 @@ type HttpDependency struct { func (x *HttpDependency) Reset() { *x = HttpDependency{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[23] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1639,7 +1799,7 @@ func (x *HttpDependency) String() string { func (*HttpDependency) ProtoMessage() {} func (x *HttpDependency) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[23] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1652,7 +1812,7 @@ func (x *HttpDependency) ProtoReflect() protoreflect.Message { // Deprecated: Use HttpDependency.ProtoReflect.Descriptor instead. func (*HttpDependency) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{23} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{25} } func (x *HttpDependency) GetName() string { @@ -1695,7 +1855,7 @@ type JobSpecHook struct { func (x *JobSpecHook) Reset() { *x = JobSpecHook{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[24] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1708,7 +1868,7 @@ func (x *JobSpecHook) String() string { func (*JobSpecHook) ProtoMessage() {} func (x *JobSpecHook) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[24] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1721,7 +1881,7 @@ func (x *JobSpecHook) ProtoReflect() protoreflect.Message { // Deprecated: Use JobSpecHook.ProtoReflect.Descriptor instead. func (*JobSpecHook) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{24} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{26} } func (x *JobSpecHook) GetName() string { @@ -1750,7 +1910,7 @@ type JobConfigItem struct { func (x *JobConfigItem) Reset() { *x = JobConfigItem{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[25] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1763,7 +1923,7 @@ func (x *JobConfigItem) String() string { func (*JobConfigItem) ProtoMessage() {} func (x *JobConfigItem) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[25] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1776,7 +1936,7 @@ func (x *JobConfigItem) ProtoReflect() protoreflect.Message { // Deprecated: Use JobConfigItem.ProtoReflect.Descriptor instead. func (*JobConfigItem) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{25} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{27} } func (x *JobConfigItem) GetName() string { @@ -1798,14 +1958,14 @@ type JobEvent struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Type JobEvent_Type `protobuf:"varint,1,opt,name=type,proto3,enum=odpf.optimus.core.v1beta1.JobEvent_Type" json:"type,omitempty"` + Type JobEvent_Type `protobuf:"varint,1,opt,name=type,proto3,enum=raystack.optimus.core.v1beta1.JobEvent_Type" json:"type,omitempty"` Value *structpb.Struct `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *JobEvent) Reset() { *x = JobEvent{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[26] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1818,7 +1978,7 @@ func (x *JobEvent) String() string { func (*JobEvent) ProtoMessage() {} func (x *JobEvent) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[26] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1831,7 +1991,7 @@ func (x *JobEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use JobEvent.ProtoReflect.Descriptor instead. func (*JobEvent) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{26} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{28} } func (x *JobEvent) GetType() JobEvent_Type { @@ -1860,7 +2020,7 @@ type JobMetadata struct { func (x *JobMetadata) Reset() { *x = JobMetadata{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[27] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1873,7 +2033,7 @@ func (x *JobMetadata) String() string { func (*JobMetadata) ProtoMessage() {} func (x *JobMetadata) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[27] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1886,7 +2046,7 @@ func (x *JobMetadata) ProtoReflect() protoreflect.Message { // Deprecated: Use JobMetadata.ProtoReflect.Descriptor instead. func (*JobMetadata) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{27} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{29} } func (x *JobMetadata) GetResource() *JobSpecMetadataResource { @@ -1915,7 +2075,7 @@ type JobSpecMetadataResource struct { func (x *JobSpecMetadataResource) Reset() { *x = JobSpecMetadataResource{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[28] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1928,7 +2088,7 @@ func (x *JobSpecMetadataResource) String() string { func (*JobSpecMetadataResource) ProtoMessage() {} func (x *JobSpecMetadataResource) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[28] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1941,7 +2101,7 @@ func (x *JobSpecMetadataResource) ProtoReflect() protoreflect.Message { // Deprecated: Use JobSpecMetadataResource.ProtoReflect.Descriptor instead. func (*JobSpecMetadataResource) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{28} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{30} } func (x *JobSpecMetadataResource) GetRequest() *JobSpecMetadataResourceConfig { @@ -1970,7 +2130,7 @@ type JobSpecMetadataResourceConfig struct { func (x *JobSpecMetadataResourceConfig) Reset() { *x = JobSpecMetadataResourceConfig{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[29] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1983,7 +2143,7 @@ func (x *JobSpecMetadataResourceConfig) String() string { func (*JobSpecMetadataResourceConfig) ProtoMessage() {} func (x *JobSpecMetadataResourceConfig) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[29] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1996,7 +2156,7 @@ func (x *JobSpecMetadataResourceConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use JobSpecMetadataResourceConfig.ProtoReflect.Descriptor instead. func (*JobSpecMetadataResourceConfig) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{29} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{31} } func (x *JobSpecMetadataResourceConfig) GetCpu() string { @@ -2025,7 +2185,7 @@ type JobSpecMetadataAirflow struct { func (x *JobSpecMetadataAirflow) Reset() { *x = JobSpecMetadataAirflow{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[30] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2038,7 +2198,7 @@ func (x *JobSpecMetadataAirflow) String() string { func (*JobSpecMetadataAirflow) ProtoMessage() {} func (x *JobSpecMetadataAirflow) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[30] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2051,7 +2211,7 @@ func (x *JobSpecMetadataAirflow) ProtoReflect() protoreflect.Message { // Deprecated: Use JobSpecMetadataAirflow.ProtoReflect.Descriptor instead. func (*JobSpecMetadataAirflow) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{30} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{32} } func (x *JobSpecMetadataAirflow) GetPool() string { @@ -2081,7 +2241,7 @@ type RefreshJobsRequest struct { func (x *RefreshJobsRequest) Reset() { *x = RefreshJobsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[31] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2094,7 +2254,7 @@ func (x *RefreshJobsRequest) String() string { func (*RefreshJobsRequest) ProtoMessage() {} func (x *RefreshJobsRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[31] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2107,7 +2267,7 @@ func (x *RefreshJobsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RefreshJobsRequest.ProtoReflect.Descriptor instead. func (*RefreshJobsRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{31} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{33} } func (x *RefreshJobsRequest) GetProjectName() string { @@ -2142,7 +2302,7 @@ type RefreshJobsResponse struct { func (x *RefreshJobsResponse) Reset() { *x = RefreshJobsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[32] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2155,7 +2315,7 @@ func (x *RefreshJobsResponse) String() string { func (*RefreshJobsResponse) ProtoMessage() {} func (x *RefreshJobsResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[32] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2168,7 +2328,7 @@ func (x *RefreshJobsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RefreshJobsResponse.ProtoReflect.Descriptor instead. func (*RefreshJobsResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{32} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{34} } func (x *RefreshJobsResponse) GetLogStatus() *Log { @@ -2189,7 +2349,7 @@ type GetDeployJobsStatusRequest struct { func (x *GetDeployJobsStatusRequest) Reset() { *x = GetDeployJobsStatusRequest{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[33] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2202,7 +2362,7 @@ func (x *GetDeployJobsStatusRequest) String() string { func (*GetDeployJobsStatusRequest) ProtoMessage() {} func (x *GetDeployJobsStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[33] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2215,7 +2375,7 @@ func (x *GetDeployJobsStatusRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetDeployJobsStatusRequest.ProtoReflect.Descriptor instead. func (*GetDeployJobsStatusRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{33} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{35} } func (x *GetDeployJobsStatusRequest) GetDeployId() string { @@ -2240,7 +2400,7 @@ type GetDeployJobsStatusResponse struct { func (x *GetDeployJobsStatusResponse) Reset() { *x = GetDeployJobsStatusResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[34] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2253,7 +2413,7 @@ func (x *GetDeployJobsStatusResponse) String() string { func (*GetDeployJobsStatusResponse) ProtoMessage() {} func (x *GetDeployJobsStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[34] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2266,7 +2426,7 @@ func (x *GetDeployJobsStatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetDeployJobsStatusResponse.ProtoReflect.Descriptor instead. func (*GetDeployJobsStatusResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{34} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{36} } func (x *GetDeployJobsStatusResponse) GetStatus() string { @@ -2316,7 +2476,7 @@ type DeployJobFailure struct { func (x *DeployJobFailure) Reset() { *x = DeployJobFailure{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[35] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2329,7 +2489,7 @@ func (x *DeployJobFailure) String() string { func (*DeployJobFailure) ProtoMessage() {} func (x *DeployJobFailure) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[35] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2342,7 +2502,7 @@ func (x *DeployJobFailure) ProtoReflect() protoreflect.Message { // Deprecated: Use DeployJobFailure.ProtoReflect.Descriptor instead. func (*DeployJobFailure) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{35} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{37} } func (x *DeployJobFailure) GetJobName() string { @@ -2373,7 +2533,7 @@ type GetJobSpecificationsRequest struct { func (x *GetJobSpecificationsRequest) Reset() { *x = GetJobSpecificationsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[36] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2386,7 +2546,7 @@ func (x *GetJobSpecificationsRequest) String() string { func (*GetJobSpecificationsRequest) ProtoMessage() {} func (x *GetJobSpecificationsRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[36] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2399,7 +2559,7 @@ func (x *GetJobSpecificationsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetJobSpecificationsRequest.ProtoReflect.Descriptor instead. func (*GetJobSpecificationsRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{36} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{38} } func (x *GetJobSpecificationsRequest) GetProjectName() string { @@ -2443,7 +2603,7 @@ type GetJobSpecificationsResponse struct { func (x *GetJobSpecificationsResponse) Reset() { *x = GetJobSpecificationsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[37] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2456,7 +2616,7 @@ func (x *GetJobSpecificationsResponse) String() string { func (*GetJobSpecificationsResponse) ProtoMessage() {} func (x *GetJobSpecificationsResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[37] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2469,7 +2629,7 @@ func (x *GetJobSpecificationsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetJobSpecificationsResponse.ProtoReflect.Descriptor instead. func (*GetJobSpecificationsResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{37} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{39} } // Deprecated: Do not use. @@ -2500,7 +2660,7 @@ type JobSpecificationResponse struct { func (x *JobSpecificationResponse) Reset() { *x = JobSpecificationResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[38] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2513,7 +2673,7 @@ func (x *JobSpecificationResponse) String() string { func (*JobSpecificationResponse) ProtoMessage() {} func (x *JobSpecificationResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[38] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2526,7 +2686,7 @@ func (x *JobSpecificationResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use JobSpecificationResponse.ProtoReflect.Descriptor instead. func (*JobSpecificationResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{38} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{40} } func (x *JobSpecificationResponse) GetProjectName() string { @@ -2563,7 +2723,7 @@ type ReplaceAllJobSpecificationsRequest struct { func (x *ReplaceAllJobSpecificationsRequest) Reset() { *x = ReplaceAllJobSpecificationsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[39] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2576,7 +2736,7 @@ func (x *ReplaceAllJobSpecificationsRequest) String() string { func (*ReplaceAllJobSpecificationsRequest) ProtoMessage() {} func (x *ReplaceAllJobSpecificationsRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[39] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[41] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2589,7 +2749,7 @@ func (x *ReplaceAllJobSpecificationsRequest) ProtoReflect() protoreflect.Message // Deprecated: Use ReplaceAllJobSpecificationsRequest.ProtoReflect.Descriptor instead. func (*ReplaceAllJobSpecificationsRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{39} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{41} } func (x *ReplaceAllJobSpecificationsRequest) GetProjectName() string { @@ -2624,7 +2784,7 @@ type ReplaceAllJobSpecificationsResponse struct { func (x *ReplaceAllJobSpecificationsResponse) Reset() { *x = ReplaceAllJobSpecificationsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[40] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2637,7 +2797,7 @@ func (x *ReplaceAllJobSpecificationsResponse) String() string { func (*ReplaceAllJobSpecificationsResponse) ProtoMessage() {} func (x *ReplaceAllJobSpecificationsResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[40] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[42] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2650,7 +2810,7 @@ func (x *ReplaceAllJobSpecificationsResponse) ProtoReflect() protoreflect.Messag // Deprecated: Use ReplaceAllJobSpecificationsResponse.ProtoReflect.Descriptor instead. func (*ReplaceAllJobSpecificationsResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{40} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{42} } func (x *ReplaceAllJobSpecificationsResponse) GetLogStatus() *Log { @@ -2673,7 +2833,7 @@ type GetJobTaskRequest struct { func (x *GetJobTaskRequest) Reset() { *x = GetJobTaskRequest{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[41] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2686,7 +2846,7 @@ func (x *GetJobTaskRequest) String() string { func (*GetJobTaskRequest) ProtoMessage() {} func (x *GetJobTaskRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[41] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2699,7 +2859,7 @@ func (x *GetJobTaskRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetJobTaskRequest.ProtoReflect.Descriptor instead. func (*GetJobTaskRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{41} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{43} } func (x *GetJobTaskRequest) GetProjectName() string { @@ -2734,7 +2894,7 @@ type GetJobTaskResponse struct { func (x *GetJobTaskResponse) Reset() { *x = GetJobTaskResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[42] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2747,7 +2907,7 @@ func (x *GetJobTaskResponse) String() string { func (*GetJobTaskResponse) ProtoMessage() {} func (x *GetJobTaskResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[42] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2760,7 +2920,7 @@ func (x *GetJobTaskResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetJobTaskResponse.ProtoReflect.Descriptor instead. func (*GetJobTaskResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{42} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{44} } func (x *GetJobTaskResponse) GetTask() *JobTask { @@ -2787,7 +2947,7 @@ type JobTask struct { func (x *JobTask) Reset() { *x = JobTask{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[43] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2800,7 +2960,7 @@ func (x *JobTask) String() string { func (*JobTask) ProtoMessage() {} func (x *JobTask) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[43] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2813,7 +2973,7 @@ func (x *JobTask) ProtoReflect() protoreflect.Message { // Deprecated: Use JobTask.ProtoReflect.Descriptor instead. func (*JobTask) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{43} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{45} } func (x *JobTask) GetName() string { @@ -2866,7 +3026,7 @@ type GetWindowRequest struct { func (x *GetWindowRequest) Reset() { *x = GetWindowRequest{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[44] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2879,7 +3039,7 @@ func (x *GetWindowRequest) String() string { func (*GetWindowRequest) ProtoMessage() {} func (x *GetWindowRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[44] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[46] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2892,7 +3052,7 @@ func (x *GetWindowRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetWindowRequest.ProtoReflect.Descriptor instead. func (*GetWindowRequest) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{44} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{46} } func (x *GetWindowRequest) GetScheduledAt() *timestamppb.Timestamp { @@ -2942,7 +3102,7 @@ type GetWindowResponse struct { func (x *GetWindowResponse) Reset() { *x = GetWindowResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[45] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2955,7 +3115,7 @@ func (x *GetWindowResponse) String() string { func (*GetWindowResponse) ProtoMessage() {} func (x *GetWindowResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[45] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[47] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2968,7 +3128,7 @@ func (x *GetWindowResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetWindowResponse.ProtoReflect.Descriptor instead. func (*GetWindowResponse) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{45} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{47} } func (x *GetWindowResponse) GetStart() *timestamppb.Timestamp { @@ -2985,34 +3145,35 @@ func (x *GetWindowResponse) GetEnd() *timestamppb.Timestamp { return nil } -type JobInspectResponse_BasicInfoSection struct { +type UpdateJobsStateRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Job *JobSpecification `protobuf:"bytes,1,opt,name=job,proto3" json:"job,omitempty"` - Source []string `protobuf:"bytes,2,rep,name=source,proto3" json:"source,omitempty"` - Destination string `protobuf:"bytes,3,opt,name=destination,proto3" json:"destination,omitempty"` - Notice []*Log `protobuf:"bytes,4,rep,name=notice,proto3" json:"notice,omitempty"` + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + NamespaceName string `protobuf:"bytes,2,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` + Remark string `protobuf:"bytes,3,opt,name=remark,proto3" json:"remark,omitempty"` + State JobState `protobuf:"varint,4,opt,name=state,proto3,enum=raystack.optimus.core.v1beta1.JobState" json:"state,omitempty"` + JobNames []string `protobuf:"bytes,5,rep,name=job_names,json=jobNames,proto3" json:"job_names,omitempty"` } -func (x *JobInspectResponse_BasicInfoSection) Reset() { - *x = JobInspectResponse_BasicInfoSection{} +func (x *UpdateJobsStateRequest) Reset() { + *x = UpdateJobsStateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[46] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *JobInspectResponse_BasicInfoSection) String() string { +func (x *UpdateJobsStateRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*JobInspectResponse_BasicInfoSection) ProtoMessage() {} +func (*UpdateJobsStateRequest) ProtoMessage() {} -func (x *JobInspectResponse_BasicInfoSection) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[46] +func (x *UpdateJobsStateRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[48] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3023,69 +3184,69 @@ func (x *JobInspectResponse_BasicInfoSection) ProtoReflect() protoreflect.Messag return mi.MessageOf(x) } -// Deprecated: Use JobInspectResponse_BasicInfoSection.ProtoReflect.Descriptor instead. -func (*JobInspectResponse_BasicInfoSection) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{8, 0} +// Deprecated: Use UpdateJobsStateRequest.ProtoReflect.Descriptor instead. +func (*UpdateJobsStateRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{48} } -func (x *JobInspectResponse_BasicInfoSection) GetJob() *JobSpecification { +func (x *UpdateJobsStateRequest) GetProjectName() string { if x != nil { - return x.Job + return x.ProjectName } - return nil + return "" } -func (x *JobInspectResponse_BasicInfoSection) GetSource() []string { +func (x *UpdateJobsStateRequest) GetNamespaceName() string { if x != nil { - return x.Source + return x.NamespaceName } - return nil + return "" } -func (x *JobInspectResponse_BasicInfoSection) GetDestination() string { +func (x *UpdateJobsStateRequest) GetRemark() string { if x != nil { - return x.Destination + return x.Remark } return "" } -func (x *JobInspectResponse_BasicInfoSection) GetNotice() []*Log { +func (x *UpdateJobsStateRequest) GetState() JobState { if x != nil { - return x.Notice + return x.State + } + return JobState_JOB_STATE_UNSPECIFIED +} + +func (x *UpdateJobsStateRequest) GetJobNames() []string { + if x != nil { + return x.JobNames } return nil } -type JobInspectResponse_JobDependency struct { +type UpdateJobsStateResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Host string `protobuf:"bytes,2,opt,name=host,proto3" json:"host,omitempty"` - ProjectName string `protobuf:"bytes,3,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` - NamespaceName string `protobuf:"bytes,4,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` - TaskName string `protobuf:"bytes,5,opt,name=task_name,json=taskName,proto3" json:"task_name,omitempty"` - Runs []*JobRun `protobuf:"bytes,6,rep,name=runs,proto3" json:"runs,omitempty"` } -func (x *JobInspectResponse_JobDependency) Reset() { - *x = JobInspectResponse_JobDependency{} +func (x *UpdateJobsStateResponse) Reset() { + *x = UpdateJobsStateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[47] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *JobInspectResponse_JobDependency) String() string { +func (x *UpdateJobsStateResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*JobInspectResponse_JobDependency) ProtoMessage() {} +func (*UpdateJobsStateResponse) ProtoMessage() {} -func (x *JobInspectResponse_JobDependency) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[47] +func (x *UpdateJobsStateResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[49] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3096,82 +3257,38 @@ func (x *JobInspectResponse_JobDependency) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use JobInspectResponse_JobDependency.ProtoReflect.Descriptor instead. -func (*JobInspectResponse_JobDependency) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{8, 1} -} - -func (x *JobInspectResponse_JobDependency) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *JobInspectResponse_JobDependency) GetHost() string { - if x != nil { - return x.Host - } - return "" -} - -func (x *JobInspectResponse_JobDependency) GetProjectName() string { - if x != nil { - return x.ProjectName - } - return "" -} - -func (x *JobInspectResponse_JobDependency) GetNamespaceName() string { - if x != nil { - return x.NamespaceName - } - return "" -} - -func (x *JobInspectResponse_JobDependency) GetTaskName() string { - if x != nil { - return x.TaskName - } - return "" -} - -func (x *JobInspectResponse_JobDependency) GetRuns() []*JobRun { - if x != nil { - return x.Runs - } - return nil +// Deprecated: Use UpdateJobsStateResponse.ProtoReflect.Descriptor instead. +func (*UpdateJobsStateResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{49} } -type JobInspectResponse_UpstreamSection struct { +type SyncJobsStateRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ExternalDependency []*JobInspectResponse_JobDependency `protobuf:"bytes,1,rep,name=external_dependency,json=externalDependency,proto3" json:"external_dependency,omitempty"` - InternalDependency []*JobInspectResponse_JobDependency `protobuf:"bytes,2,rep,name=internal_dependency,json=internalDependency,proto3" json:"internal_dependency,omitempty"` - HttpDependency []*HttpDependency `protobuf:"bytes,3,rep,name=http_dependency,json=httpDependency,proto3" json:"http_dependency,omitempty"` - UnknownDependencies []*JobInspectResponse_UpstreamSection_UnknownDependencies `protobuf:"bytes,4,rep,name=unknown_dependencies,json=unknownDependencies,proto3" json:"unknown_dependencies,omitempty"` - Notice []*Log `protobuf:"bytes,5,rep,name=notice,proto3" json:"notice,omitempty"` + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + NamespaceName string `protobuf:"bytes,2,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` + JobStates []*SyncJobsStateRequest_JobStatePair `protobuf:"bytes,3,rep,name=job_states,json=jobStates,proto3" json:"job_states,omitempty"` } -func (x *JobInspectResponse_UpstreamSection) Reset() { - *x = JobInspectResponse_UpstreamSection{} +func (x *SyncJobsStateRequest) Reset() { + *x = SyncJobsStateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[48] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *JobInspectResponse_UpstreamSection) String() string { +func (x *SyncJobsStateRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*JobInspectResponse_UpstreamSection) ProtoMessage() {} +func (*SyncJobsStateRequest) ProtoMessage() {} -func (x *JobInspectResponse_UpstreamSection) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[48] +func (x *SyncJobsStateRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[50] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3182,72 +3299,55 @@ func (x *JobInspectResponse_UpstreamSection) ProtoReflect() protoreflect.Message return mi.MessageOf(x) } -// Deprecated: Use JobInspectResponse_UpstreamSection.ProtoReflect.Descriptor instead. -func (*JobInspectResponse_UpstreamSection) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{8, 2} -} - -func (x *JobInspectResponse_UpstreamSection) GetExternalDependency() []*JobInspectResponse_JobDependency { - if x != nil { - return x.ExternalDependency - } - return nil -} - -func (x *JobInspectResponse_UpstreamSection) GetInternalDependency() []*JobInspectResponse_JobDependency { - if x != nil { - return x.InternalDependency - } - return nil +// Deprecated: Use SyncJobsStateRequest.ProtoReflect.Descriptor instead. +func (*SyncJobsStateRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{50} } -func (x *JobInspectResponse_UpstreamSection) GetHttpDependency() []*HttpDependency { +func (x *SyncJobsStateRequest) GetProjectName() string { if x != nil { - return x.HttpDependency + return x.ProjectName } - return nil + return "" } -func (x *JobInspectResponse_UpstreamSection) GetUnknownDependencies() []*JobInspectResponse_UpstreamSection_UnknownDependencies { +func (x *SyncJobsStateRequest) GetNamespaceName() string { if x != nil { - return x.UnknownDependencies + return x.NamespaceName } - return nil + return "" } -func (x *JobInspectResponse_UpstreamSection) GetNotice() []*Log { +func (x *SyncJobsStateRequest) GetJobStates() []*SyncJobsStateRequest_JobStatePair { if x != nil { - return x.Notice + return x.JobStates } return nil } -type JobInspectResponse_DownstreamSection struct { +type SyncJobsStateResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - - DownstreamJobs []*JobInspectResponse_JobDependency `protobuf:"bytes,1,rep,name=downstream_jobs,json=downstreamJobs,proto3" json:"downstream_jobs,omitempty"` - Notice []*Log `protobuf:"bytes,2,rep,name=notice,proto3" json:"notice,omitempty"` } -func (x *JobInspectResponse_DownstreamSection) Reset() { - *x = JobInspectResponse_DownstreamSection{} +func (x *SyncJobsStateResponse) Reset() { + *x = SyncJobsStateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[49] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *JobInspectResponse_DownstreamSection) String() string { +func (x *SyncJobsStateResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*JobInspectResponse_DownstreamSection) ProtoMessage() {} +func (*SyncJobsStateResponse) ProtoMessage() {} -func (x *JobInspectResponse_DownstreamSection) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[49] +func (x *SyncJobsStateResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3258,20 +3358,298 @@ func (x *JobInspectResponse_DownstreamSection) ProtoReflect() protoreflect.Messa return mi.MessageOf(x) } -// Deprecated: Use JobInspectResponse_DownstreamSection.ProtoReflect.Descriptor instead. -func (*JobInspectResponse_DownstreamSection) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{8, 3} +// Deprecated: Use SyncJobsStateResponse.ProtoReflect.Descriptor instead. +func (*SyncJobsStateResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{51} } -func (x *JobInspectResponse_DownstreamSection) GetDownstreamJobs() []*JobInspectResponse_JobDependency { - if x != nil { - return x.DownstreamJobs - } - return nil -} +type JobInspectResponse_BasicInfoSection struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (x *JobInspectResponse_DownstreamSection) GetNotice() []*Log { - if x != nil { + Job *JobSpecification `protobuf:"bytes,1,opt,name=job,proto3" json:"job,omitempty"` + Source []string `protobuf:"bytes,2,rep,name=source,proto3" json:"source,omitempty"` + Destination string `protobuf:"bytes,3,opt,name=destination,proto3" json:"destination,omitempty"` + Notice []*Log `protobuf:"bytes,4,rep,name=notice,proto3" json:"notice,omitempty"` +} + +func (x *JobInspectResponse_BasicInfoSection) Reset() { + *x = JobInspectResponse_BasicInfoSection{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[52] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *JobInspectResponse_BasicInfoSection) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JobInspectResponse_BasicInfoSection) ProtoMessage() {} + +func (x *JobInspectResponse_BasicInfoSection) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[52] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JobInspectResponse_BasicInfoSection.ProtoReflect.Descriptor instead. +func (*JobInspectResponse_BasicInfoSection) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{8, 0} +} + +func (x *JobInspectResponse_BasicInfoSection) GetJob() *JobSpecification { + if x != nil { + return x.Job + } + return nil +} + +func (x *JobInspectResponse_BasicInfoSection) GetSource() []string { + if x != nil { + return x.Source + } + return nil +} + +func (x *JobInspectResponse_BasicInfoSection) GetDestination() string { + if x != nil { + return x.Destination + } + return "" +} + +func (x *JobInspectResponse_BasicInfoSection) GetNotice() []*Log { + if x != nil { + return x.Notice + } + return nil +} + +type JobInspectResponse_JobDependency struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Host string `protobuf:"bytes,2,opt,name=host,proto3" json:"host,omitempty"` + ProjectName string `protobuf:"bytes,3,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + NamespaceName string `protobuf:"bytes,4,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` + TaskName string `protobuf:"bytes,5,opt,name=task_name,json=taskName,proto3" json:"task_name,omitempty"` + Runs []*JobRun `protobuf:"bytes,6,rep,name=runs,proto3" json:"runs,omitempty"` +} + +func (x *JobInspectResponse_JobDependency) Reset() { + *x = JobInspectResponse_JobDependency{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[53] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *JobInspectResponse_JobDependency) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JobInspectResponse_JobDependency) ProtoMessage() {} + +func (x *JobInspectResponse_JobDependency) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[53] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JobInspectResponse_JobDependency.ProtoReflect.Descriptor instead. +func (*JobInspectResponse_JobDependency) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{8, 1} +} + +func (x *JobInspectResponse_JobDependency) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *JobInspectResponse_JobDependency) GetHost() string { + if x != nil { + return x.Host + } + return "" +} + +func (x *JobInspectResponse_JobDependency) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *JobInspectResponse_JobDependency) GetNamespaceName() string { + if x != nil { + return x.NamespaceName + } + return "" +} + +func (x *JobInspectResponse_JobDependency) GetTaskName() string { + if x != nil { + return x.TaskName + } + return "" +} + +func (x *JobInspectResponse_JobDependency) GetRuns() []*JobRun { + if x != nil { + return x.Runs + } + return nil +} + +type JobInspectResponse_UpstreamSection struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ExternalDependency []*JobInspectResponse_JobDependency `protobuf:"bytes,1,rep,name=external_dependency,json=externalDependency,proto3" json:"external_dependency,omitempty"` + InternalDependency []*JobInspectResponse_JobDependency `protobuf:"bytes,2,rep,name=internal_dependency,json=internalDependency,proto3" json:"internal_dependency,omitempty"` + HttpDependency []*HttpDependency `protobuf:"bytes,3,rep,name=http_dependency,json=httpDependency,proto3" json:"http_dependency,omitempty"` + UnknownDependencies []*JobInspectResponse_UpstreamSection_UnknownDependencies `protobuf:"bytes,4,rep,name=unknown_dependencies,json=unknownDependencies,proto3" json:"unknown_dependencies,omitempty"` + Notice []*Log `protobuf:"bytes,5,rep,name=notice,proto3" json:"notice,omitempty"` +} + +func (x *JobInspectResponse_UpstreamSection) Reset() { + *x = JobInspectResponse_UpstreamSection{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[54] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *JobInspectResponse_UpstreamSection) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JobInspectResponse_UpstreamSection) ProtoMessage() {} + +func (x *JobInspectResponse_UpstreamSection) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[54] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JobInspectResponse_UpstreamSection.ProtoReflect.Descriptor instead. +func (*JobInspectResponse_UpstreamSection) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{8, 2} +} + +func (x *JobInspectResponse_UpstreamSection) GetExternalDependency() []*JobInspectResponse_JobDependency { + if x != nil { + return x.ExternalDependency + } + return nil +} + +func (x *JobInspectResponse_UpstreamSection) GetInternalDependency() []*JobInspectResponse_JobDependency { + if x != nil { + return x.InternalDependency + } + return nil +} + +func (x *JobInspectResponse_UpstreamSection) GetHttpDependency() []*HttpDependency { + if x != nil { + return x.HttpDependency + } + return nil +} + +func (x *JobInspectResponse_UpstreamSection) GetUnknownDependencies() []*JobInspectResponse_UpstreamSection_UnknownDependencies { + if x != nil { + return x.UnknownDependencies + } + return nil +} + +func (x *JobInspectResponse_UpstreamSection) GetNotice() []*Log { + if x != nil { + return x.Notice + } + return nil +} + +type JobInspectResponse_DownstreamSection struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + DownstreamJobs []*JobInspectResponse_JobDependency `protobuf:"bytes,1,rep,name=downstream_jobs,json=downstreamJobs,proto3" json:"downstream_jobs,omitempty"` + Notice []*Log `protobuf:"bytes,2,rep,name=notice,proto3" json:"notice,omitempty"` +} + +func (x *JobInspectResponse_DownstreamSection) Reset() { + *x = JobInspectResponse_DownstreamSection{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[55] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *JobInspectResponse_DownstreamSection) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JobInspectResponse_DownstreamSection) ProtoMessage() {} + +func (x *JobInspectResponse_DownstreamSection) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[55] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JobInspectResponse_DownstreamSection.ProtoReflect.Descriptor instead. +func (*JobInspectResponse_DownstreamSection) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{8, 3} +} + +func (x *JobInspectResponse_DownstreamSection) GetDownstreamJobs() []*JobInspectResponse_JobDependency { + if x != nil { + return x.DownstreamJobs + } + return nil +} + +func (x *JobInspectResponse_DownstreamSection) GetNotice() []*Log { + if x != nil { return x.Notice } return nil @@ -3290,7 +3668,7 @@ type JobInspectResponse_UpstreamSection_UnknownDependencies struct { func (x *JobInspectResponse_UpstreamSection_UnknownDependencies) Reset() { *x = JobInspectResponse_UpstreamSection_UnknownDependencies{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[50] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3303,7 +3681,7 @@ func (x *JobInspectResponse_UpstreamSection_UnknownDependencies) String() string func (*JobInspectResponse_UpstreamSection_UnknownDependencies) ProtoMessage() {} func (x *JobInspectResponse_UpstreamSection_UnknownDependencies) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[50] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[56] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3316,7 +3694,7 @@ func (x *JobInspectResponse_UpstreamSection_UnknownDependencies) ProtoReflect() // Deprecated: Use JobInspectResponse_UpstreamSection_UnknownDependencies.ProtoReflect.Descriptor instead. func (*JobInspectResponse_UpstreamSection_UnknownDependencies) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{8, 2, 0} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{8, 2, 0} } func (x *JobInspectResponse_UpstreamSection_UnknownDependencies) GetJobName() string { @@ -3352,7 +3730,7 @@ type JobSpecification_Behavior struct { func (x *JobSpecification_Behavior) Reset() { *x = JobSpecification_Behavior{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[53] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3365,7 +3743,7 @@ func (x *JobSpecification_Behavior) String() string { func (*JobSpecification_Behavior) ProtoMessage() {} func (x *JobSpecification_Behavior) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[53] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[59] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3378,7 +3756,7 @@ func (x *JobSpecification_Behavior) ProtoReflect() protoreflect.Message { // Deprecated: Use JobSpecification_Behavior.ProtoReflect.Descriptor instead. func (*JobSpecification_Behavior) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{21, 2} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{23, 2} } func (x *JobSpecification_Behavior) GetRetry() *JobSpecification_Behavior_Retry { @@ -3409,7 +3787,7 @@ type JobSpecification_Behavior_Retry struct { func (x *JobSpecification_Behavior_Retry) Reset() { *x = JobSpecification_Behavior_Retry{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[54] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3422,7 +3800,7 @@ func (x *JobSpecification_Behavior_Retry) String() string { func (*JobSpecification_Behavior_Retry) ProtoMessage() {} func (x *JobSpecification_Behavior_Retry) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[54] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[60] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3435,7 +3813,7 @@ func (x *JobSpecification_Behavior_Retry) ProtoReflect() protoreflect.Message { // Deprecated: Use JobSpecification_Behavior_Retry.ProtoReflect.Descriptor instead. func (*JobSpecification_Behavior_Retry) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{21, 2, 0} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{23, 2, 0} } func (x *JobSpecification_Behavior_Retry) GetCount() int32 { @@ -3465,7 +3843,7 @@ type JobSpecification_Behavior_Notifiers struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - On JobEvent_Type `protobuf:"varint,1,opt,name=on,proto3,enum=odpf.optimus.core.v1beta1.JobEvent_Type" json:"on,omitempty"` + On JobEvent_Type `protobuf:"varint,1,opt,name=on,proto3,enum=raystack.optimus.core.v1beta1.JobEvent_Type" json:"on,omitempty"` Channels []string `protobuf:"bytes,2,rep,name=channels,proto3" json:"channels,omitempty"` Config map[string]string `protobuf:"bytes,3,rep,name=config,proto3" json:"config,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } @@ -3473,7 +3851,7 @@ type JobSpecification_Behavior_Notifiers struct { func (x *JobSpecification_Behavior_Notifiers) Reset() { *x = JobSpecification_Behavior_Notifiers{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[55] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3486,7 +3864,7 @@ func (x *JobSpecification_Behavior_Notifiers) String() string { func (*JobSpecification_Behavior_Notifiers) ProtoMessage() {} func (x *JobSpecification_Behavior_Notifiers) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[55] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[61] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3499,7 +3877,7 @@ func (x *JobSpecification_Behavior_Notifiers) ProtoReflect() protoreflect.Messag // Deprecated: Use JobSpecification_Behavior_Notifiers.ProtoReflect.Descriptor instead. func (*JobSpecification_Behavior_Notifiers) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{21, 2, 1} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{23, 2, 1} } func (x *JobSpecification_Behavior_Notifiers) GetOn() JobEvent_Type { @@ -3535,7 +3913,7 @@ type JobTask_Destination struct { func (x *JobTask_Destination) Reset() { *x = JobTask_Destination{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[60] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3548,7 +3926,7 @@ func (x *JobTask_Destination) String() string { func (*JobTask_Destination) ProtoMessage() {} func (x *JobTask_Destination) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[60] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[66] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3561,7 +3939,7 @@ func (x *JobTask_Destination) ProtoReflect() protoreflect.Message { // Deprecated: Use JobTask_Destination.ProtoReflect.Descriptor instead. func (*JobTask_Destination) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{43, 0} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{45, 0} } func (x *JobTask_Destination) GetDestination() string { @@ -3589,7 +3967,7 @@ type JobTask_Dependency struct { func (x *JobTask_Dependency) Reset() { *x = JobTask_Dependency{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[61] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3602,7 +3980,7 @@ func (x *JobTask_Dependency) String() string { func (*JobTask_Dependency) ProtoMessage() {} func (x *JobTask_Dependency) ProtoReflect() protoreflect.Message { - mi := &file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[61] + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[67] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3615,7 +3993,7 @@ func (x *JobTask_Dependency) ProtoReflect() protoreflect.Message { // Deprecated: Use JobTask_Dependency.ProtoReflect.Descriptor instead. func (*JobTask_Dependency) Descriptor() ([]byte, []int) { - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{43, 1} + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{45, 1} } func (x *JobTask_Dependency) GetDependency() string { @@ -3625,434 +4003,521 @@ func (x *JobTask_Dependency) GetDependency() string { return "" } -var File_odpf_optimus_core_v1beta1_job_spec_proto protoreflect.FileDescriptor +type SyncJobsStateRequest_JobStatePair struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -var file_odpf_optimus_core_v1beta1_job_spec_proto_rawDesc = []byte{ - 0x0a, 0x28, 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, - 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x6a, 0x6f, 0x62, 0x5f, - 0x73, 0x70, 0x65, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x6f, 0x64, 0x70, 0x66, - 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, - 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x1a, 0x26, 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, - 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, - 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xaa, 0x01, 0x0a, 0x1d, 0x44, - 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x3f, 0x0a, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, - 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, - 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73, - 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x8a, 0x01, 0x0a, 0x1e, 0x44, 0x65, 0x70, 0x6c, - 0x6f, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x65, - 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0c, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, - 0x3d, 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, - 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x4c, 0x6f, 0x67, 0x52, 0x09, 0x6c, 0x6f, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4a, 0x04, - 0x08, 0x01, 0x10, 0x07, 0x22, 0xaa, 0x01, 0x0a, 0x1b, 0x41, 0x64, 0x64, 0x4a, 0x6f, 0x62, 0x53, - 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x41, - 0x0a, 0x05, 0x73, 0x70, 0x65, 0x63, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, - 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, - 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x73, 0x70, 0x65, 0x63, - 0x73, 0x22, 0x36, 0x0a, 0x1c, 0x41, 0x64, 0x64, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x6c, 0x6f, 0x67, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x22, 0xad, 0x01, 0x0a, 0x1e, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x41, 0x0a, 0x05, 0x73, 0x70, 0x65, 0x63, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, - 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x05, 0x73, 0x70, 0x65, 0x63, 0x73, 0x22, 0x33, 0x0a, 0x1f, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, - 0x6c, 0x6f, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6c, 0x6f, 0x67, 0x22, 0xf8, - 0x01, 0x0a, 0x11, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, - 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x04, 0x73, 0x70, 0x65, - 0x63, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, - 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x3d, 0x0a, 0x0c, 0x73, 0x63, - 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x73, 0x63, - 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x41, 0x74, 0x22, 0x5d, 0x0a, 0x06, 0x4a, 0x6f, 0x62, - 0x52, 0x75, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3d, 0x0a, 0x0c, 0x73, 0x63, 0x68, - 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x73, 0x63, 0x68, - 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x41, 0x74, 0x22, 0x91, 0x0c, 0x0a, 0x12, 0x4a, 0x6f, 0x62, - 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x5d, 0x0a, 0x0a, 0x62, 0x61, 0x73, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, - 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x2e, 0x42, 0x61, 0x73, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x53, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x62, 0x61, 0x73, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x5b, - 0x0a, 0x09, 0x75, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x3d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, - 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, - 0x62, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x2e, 0x55, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x09, 0x75, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x12, 0x61, 0x0a, 0x0b, 0x64, - 0x6f, 0x77, 0x6e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x3f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, - 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, - 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x0b, 0x64, 0x6f, 0x77, 0x6e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x1a, 0xc3, - 0x01, 0x0a, 0x10, 0x42, 0x61, 0x73, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x53, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, 0x03, 0x6a, 0x6f, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x2b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, - 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x6a, - 0x6f, 0x62, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, - 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x06, - 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6f, - 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x06, 0x6e, 0x6f, - 0x74, 0x69, 0x63, 0x65, 0x1a, 0xd5, 0x01, 0x0a, 0x0d, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x70, 0x65, - 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, - 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x21, - 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x61, 0x73, 0x6b, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x61, 0x73, - 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x72, 0x75, 0x6e, 0x73, 0x18, 0x06, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, - 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x52, 0x04, 0x72, 0x75, 0x6e, 0x73, 0x1a, 0x89, 0x05, 0x0a, - 0x0f, 0x55, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x6c, 0x0a, 0x13, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x70, - 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, - 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x73, - 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4a, 0x6f, 0x62, - 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x12, 0x65, 0x78, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x6c, - 0x0a, 0x13, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x70, 0x65, 0x6e, - 0x64, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x6f, 0x64, - 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x73, 0x70, 0x65, - 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x65, - 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x12, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x52, 0x0a, 0x0f, - 0x68, 0x74, 0x74, 0x70, 0x5f, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, - 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, - 0x52, 0x0e, 0x68, 0x74, 0x74, 0x70, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, - 0x12, 0x84, 0x01, 0x0a, 0x14, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x5f, 0x64, 0x65, 0x70, - 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x51, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x49, - 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x55, - 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x55, - 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, - 0x65, 0x73, 0x52, 0x13, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x44, 0x65, 0x70, 0x65, 0x6e, - 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, 0x36, 0x0a, 0x06, 0x6e, 0x6f, 0x74, 0x69, 0x63, - 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, + JobName string `protobuf:"bytes,1,opt,name=job_name,json=jobName,proto3" json:"job_name,omitempty"` + State JobState `protobuf:"varint,2,opt,name=state,proto3,enum=raystack.optimus.core.v1beta1.JobState" json:"state,omitempty"` +} + +func (x *SyncJobsStateRequest_JobStatePair) Reset() { + *x = SyncJobsStateRequest_JobStatePair{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[68] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SyncJobsStateRequest_JobStatePair) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncJobsStateRequest_JobStatePair) ProtoMessage() {} + +func (x *SyncJobsStateRequest_JobStatePair) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[68] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SyncJobsStateRequest_JobStatePair.ProtoReflect.Descriptor instead. +func (*SyncJobsStateRequest_JobStatePair) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP(), []int{50, 0} +} + +func (x *SyncJobsStateRequest_JobStatePair) GetJobName() string { + if x != nil { + return x.JobName + } + return "" +} + +func (x *SyncJobsStateRequest_JobStatePair) GetState() JobState { + if x != nil { + return x.State + } + return JobState_JOB_STATE_UNSPECIFIED +} + +var File_raystack_optimus_core_v1beta1_job_spec_proto protoreflect.FileDescriptor + +var file_raystack_optimus_core_v1beta1_job_spec_proto_rawDesc = []byte{ + 0x0a, 0x2f, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2f, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2f, 0x6a, 0x6f, 0x62, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x20, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x06, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x1a, - 0x86, 0x01, 0x0a, 0x13, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x44, 0x65, 0x70, 0x65, 0x6e, - 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x31, 0x0a, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x13, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x73, - 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0xb1, 0x01, 0x0a, 0x11, 0x44, 0x6f, 0x77, - 0x6e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x64, - 0x0a, 0x0f, 0x64, 0x6f, 0x77, 0x6e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x6a, 0x6f, 0x62, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, + 0x74, 0x61, 0x31, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, + 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x2d, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2f, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, + 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, + 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, + 0xb1, 0x01, 0x0a, 0x1d, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, + 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x46, 0x0a, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x12, 0x25, 0x0a, 0x0e, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, + 0x61, 0x6d, 0x65, 0x22, 0x91, 0x01, 0x0a, 0x1e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, + 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x64, + 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x44, 0x0a, 0x0a, 0x6c, + 0x6f, 0x67, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x25, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x09, 0x6c, 0x6f, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x07, 0x22, 0xb1, 0x01, 0x0a, 0x1b, 0x41, 0x64, 0x64, 0x4a, + 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x48, 0x0a, 0x05, 0x73, 0x70, 0x65, 0x63, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, - 0x65, 0x6e, 0x63, 0x79, 0x52, 0x0e, 0x64, 0x6f, 0x77, 0x6e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x36, 0x0a, 0x06, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, - 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x06, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x22, 0xaa, 0x01, 0x0a, - 0x1d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, - 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, + 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x73, 0x70, 0x65, 0x63, 0x73, 0x22, 0x36, 0x0a, 0x1c, 0x41, + 0x64, 0x64, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6c, + 0x6f, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6c, 0x6f, 0x67, 0x4a, 0x04, 0x08, + 0x01, 0x10, 0x02, 0x22, 0xb4, 0x01, 0x0a, 0x1e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, + 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x48, 0x0a, 0x05, 0x73, 0x70, 0x65, 0x63, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x22, 0x54, 0x0a, 0x1e, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, - 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, - 0x81, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, - 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e, - 0x61, 0x6d, 0x65, 0x22, 0x5e, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, - 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x2b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, - 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x73, - 0x70, 0x65, 0x63, 0x22, 0xbf, 0x01, 0x0a, 0x1d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, - 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x73, 0x70, 0x65, 0x63, 0x73, 0x22, 0x33, 0x0a, 0x1f, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, + 0x03, 0x6c, 0x6f, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6c, 0x6f, 0x67, 0x22, + 0xff, 0x01, 0x0a, 0x11, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6c, - 0x65, 0x61, 0x6e, 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0c, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, - 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, - 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22, 0x54, 0x0a, 0x1e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4a, + 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x46, 0x0a, 0x04, 0x73, 0x70, + 0x65, 0x63, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, + 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x73, 0x70, + 0x65, 0x63, 0x12, 0x3d, 0x0a, 0x0c, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x5f, + 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x41, + 0x74, 0x22, 0x5d, 0x0a, 0x06, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x3d, 0x0a, 0x0c, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x5f, 0x61, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x41, 0x74, + 0x22, 0xec, 0x0c, 0x0a, 0x12, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x0a, 0x62, 0x61, 0x73, 0x69, 0x63, + 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x45, 0x2e, 0x67, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, + 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, + 0x6f, 0x62, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x2e, 0x42, 0x61, 0x73, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x53, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x09, 0x62, 0x61, 0x73, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x62, 0x0a, + 0x09, 0x75, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x44, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, + 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x55, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x75, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x73, 0x12, 0x68, 0x0a, 0x0b, 0x64, 0x6f, 0x77, 0x6e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x46, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x73, + 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x6f, 0x77, + 0x6e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, + 0x64, 0x6f, 0x77, 0x6e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x1a, 0xd1, 0x01, 0x0a, 0x10, + 0x42, 0x61, 0x73, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x44, 0x0a, 0x03, 0x6a, 0x6f, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, + 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x03, 0x6a, 0x6f, 0x62, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x20, + 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x3d, 0x0a, 0x06, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x25, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, + 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x06, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x1a, + 0xdc, 0x01, 0x0a, 0x0d, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, + 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x61, 0x73, 0x6b, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x3c, 0x0a, 0x04, 0x72, 0x75, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, + 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x52, 0x04, 0x72, 0x75, 0x6e, 0x73, 0x1a, 0xac, + 0x05, 0x0a, 0x0f, 0x55, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x73, 0x0a, 0x13, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x64, + 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x42, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, + 0x6e, 0x63, 0x79, 0x52, 0x12, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x65, 0x70, + 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x73, 0x0a, 0x13, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x42, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x73, 0x70, 0x65, + 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x65, + 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x12, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x59, 0x0a, 0x0f, + 0x68, 0x74, 0x74, 0x70, 0x5f, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x44, 0x65, 0x70, + 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x0e, 0x68, 0x74, 0x74, 0x70, 0x44, 0x65, 0x70, + 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x8b, 0x01, 0x0a, 0x14, 0x75, 0x6e, 0x6b, 0x6e, + 0x6f, 0x77, 0x6e, 0x5f, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x58, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x73, + 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x55, 0x70, 0x73, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x55, 0x6e, 0x6b, + 0x6e, 0x6f, 0x77, 0x6e, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, + 0x52, 0x13, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, + 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, 0x3d, 0x0a, 0x06, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x18, + 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x06, 0x6e, 0x6f, + 0x74, 0x69, 0x63, 0x65, 0x1a, 0x86, 0x01, 0x0a, 0x13, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, + 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, 0x19, 0x0a, 0x08, + 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x6a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x31, 0x0a, 0x14, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0xbf, 0x01, + 0x0a, 0x11, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x6b, 0x0a, 0x0f, 0x64, 0x6f, 0x77, 0x6e, 0x73, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x5f, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x42, 0x2e, 0x67, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, + 0x52, 0x0e, 0x64, 0x6f, 0x77, 0x6e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4a, 0x6f, 0x62, 0x73, + 0x12, 0x3d, 0x0a, 0x06, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x25, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, + 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x06, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x22, + 0xb1, 0x01, 0x0a, 0x1d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, + 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x46, 0x0a, 0x04, 0x73, + 0x70, 0x65, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, + 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x73, + 0x70, 0x65, 0x63, 0x22, 0x54, 0x0a, 0x1e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, + 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, + 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x81, 0x01, 0x0a, 0x1a, 0x47, 0x65, + 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x65, 0x0a, + 0x1b, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x04, + 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x67, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, + 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, + 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, + 0x73, 0x70, 0x65, 0x63, 0x22, 0xbf, 0x01, 0x0a, 0x1d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x67, 0x0a, 0x1b, 0x4c, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63, + 0x6c, 0x65, 0x61, 0x6e, 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0c, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22, 0x54, 0x0a, 0x1e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xae, 0x01, 0x0a, + 0x19, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, + 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x2c, 0x0a, 0x12, 0x6e, 0x65, 0x77, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6e, 0x65, 0x77, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x1c, 0x0a, + 0x1a, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x67, 0x0a, 0x1b, 0x4c, 0x69, 0x73, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x5f, 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x4a, 0x6f, 0x62, 0x53, + 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x66, 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x22, 0xae, 0x01, 0x0a, + 0x1c, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, + 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x44, 0x0a, 0x03, 0x6a, 0x6f, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, + 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x03, 0x6a, 0x6f, 0x62, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x39, 0x0a, + 0x1d, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, + 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0xb1, 0x01, 0x0a, 0x1d, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x46, 0x0a, + 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x67, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x22, 0xa7, 0x01, 0x0a, 0x1c, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4a, - 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3d, 0x0a, 0x03, 0x6a, 0x6f, 0x62, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, + 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x6c, 0x0a, 0x1e, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, + 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x09, 0x6c, 0x6f, 0x67, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x05, 0x22, 0xe6, 0x0d, 0x0a, 0x10, 0x4a, + 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x77, + 0x6e, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, + 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, + 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x64, 0x65, 0x70, + 0x65, 0x6e, 0x64, 0x73, 0x5f, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x73, 0x74, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0d, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x4f, 0x6e, 0x50, 0x61, 0x73, + 0x74, 0x12, 0x1d, 0x0a, 0x08, 0x63, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x75, 0x70, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x63, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, + 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x61, 0x73, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x47, 0x0a, + 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, + 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x4a, 0x6f, 0x62, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, + 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x69, 0x6e, + 0x64, 0x6f, 0x77, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x77, 0x69, 0x6e, 0x64, 0x6f, + 0x77, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x2c, 0x0a, 0x12, + 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x5f, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x5f, + 0x74, 0x6f, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, + 0x54, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x54, 0x6f, 0x12, 0x53, 0x0a, 0x0c, 0x64, 0x65, + 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x2f, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, + 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, + 0x79, 0x52, 0x0c, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, + 0x56, 0x0a, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x3e, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x6a, 0x6f, 0x62, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, - 0x39, 0x0a, 0x1d, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0xaa, 0x01, 0x0a, 0x1d, 0x43, - 0x68, 0x65, 0x63, 0x6b, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x3f, 0x0a, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, - 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, + 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x43, 0x0a, 0x05, 0x68, 0x6f, 0x6f, 0x6b, 0x73, + 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, - 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73, - 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x65, 0x0a, 0x1e, 0x43, 0x68, 0x65, 0x63, 0x6b, - 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x0a, 0x6c, 0x6f, 0x67, - 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, - 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x09, 0x6c, - 0x6f, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x05, 0x22, 0x95, - 0x0d, 0x0a, 0x10, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, - 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, - 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x26, 0x0a, - 0x0f, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x5f, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x73, 0x74, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x4f, - 0x6e, 0x50, 0x61, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x75, - 0x70, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x63, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, - 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x09, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x61, 0x73, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x40, 0x0a, - 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, - 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, - 0x1f, 0x0a, 0x0b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x0b, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x53, 0x69, 0x7a, 0x65, - 0x12, 0x23, 0x0a, 0x0d, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, - 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x4f, - 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x5f, - 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x6f, 0x18, 0x0d, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x10, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x54, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, - 0x65, 0x54, 0x6f, 0x12, 0x4c, 0x0a, 0x0c, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, - 0x69, 0x65, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6f, 0x64, 0x70, 0x66, - 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, - 0x6e, 0x63, 0x79, 0x52, 0x0c, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, - 0x73, 0x12, 0x4f, 0x0a, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x37, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, - 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, - 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x73, 0x12, 0x3c, 0x0a, 0x05, 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x26, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, - 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, - 0x62, 0x53, 0x70, 0x65, 0x63, 0x48, 0x6f, 0x6f, 0x6b, 0x52, 0x05, 0x68, 0x6f, 0x6f, 0x6b, 0x73, - 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x4f, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x12, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, - 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, - 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, - 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, - 0x65, 0x6c, 0x73, 0x12, 0x50, 0x0a, 0x08, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x18, - 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, + 0x63, 0x48, 0x6f, 0x6f, 0x6b, 0x52, 0x05, 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x20, 0x0a, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x56, + 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x12, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e, + 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x2e, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x52, 0x08, 0x62, 0x65, 0x68, - 0x61, 0x76, 0x69, 0x6f, 0x72, 0x12, 0x42, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, - 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, - 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, - 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x16, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0xb8, 0x04, 0x0a, 0x08, - 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x12, 0x50, 0x0a, 0x05, 0x72, 0x65, 0x74, 0x72, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, - 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x2e, 0x52, 0x65, - 0x74, 0x72, 0x79, 0x52, 0x05, 0x72, 0x65, 0x74, 0x72, 0x79, 0x12, 0x56, 0x0a, 0x06, 0x6e, 0x6f, - 0x74, 0x69, 0x66, 0x79, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x6f, 0x64, 0x70, - 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, - 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x06, 0x6e, 0x6f, 0x74, 0x69, - 0x66, 0x79, 0x1a, 0x7f, 0x0a, 0x05, 0x52, 0x65, 0x74, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x12, 0x2f, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x64, 0x65, 0x6c, - 0x61, 0x79, 0x12, 0x2f, 0x0a, 0x13, 0x65, 0x78, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x69, 0x61, - 0x6c, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x12, 0x65, 0x78, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x42, 0x61, 0x63, 0x6b, - 0x6f, 0x66, 0x66, 0x1a, 0x80, 0x02, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, - 0x73, 0x12, 0x38, 0x0a, 0x02, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, - 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x02, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x63, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x63, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x62, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, - 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x2e, 0x4e, 0x6f, - 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x39, 0x0a, 0x0b, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x8b, 0x01, 0x0a, 0x0d, 0x4a, 0x6f, 0x62, 0x44, 0x65, - 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x12, 0x52, 0x0a, 0x0f, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, - 0x6e, 0x63, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, - 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, - 0x65, 0x6e, 0x63, 0x79, 0x52, 0x0e, 0x68, 0x74, 0x74, 0x70, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, - 0x65, 0x6e, 0x63, 0x79, 0x22, 0xce, 0x02, 0x0a, 0x0e, 0x48, 0x74, 0x74, 0x70, 0x44, 0x65, 0x70, - 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, - 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x50, 0x0a, - 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, - 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x44, - 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, - 0x4d, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x35, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x48, 0x74, 0x74, 0x70, - 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x3a, - 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x39, 0x0a, 0x0b, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x63, 0x0a, 0x0b, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, - 0x48, 0x6f, 0x6f, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x40, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, + 0x6f, 0x6e, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, + 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x57, 0x0a, 0x08, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, + 0x6f, 0x72, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, + 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x42, 0x65, 0x68, + 0x61, 0x76, 0x69, 0x6f, 0x72, 0x52, 0x08, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x12, + 0x49, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x14, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x49, 0x74, - 0x65, 0x6d, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x39, 0x0a, 0x0d, 0x4a, 0x6f, - 0x62, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, + 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x16, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0xd4, 0x04, 0x0a, + 0x08, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x12, 0x57, 0x0a, 0x05, 0x72, 0x65, 0x74, + 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, + 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x42, 0x65, 0x68, + 0x61, 0x76, 0x69, 0x6f, 0x72, 0x2e, 0x52, 0x65, 0x74, 0x72, 0x79, 0x52, 0x05, 0x72, 0x65, 0x74, + 0x72, 0x79, 0x12, 0x5d, 0x0a, 0x06, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x45, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x2e, + 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x06, 0x6e, 0x6f, 0x74, 0x69, 0x66, + 0x79, 0x1a, 0x7f, 0x0a, 0x05, 0x52, 0x65, 0x74, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x12, 0x2f, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, + 0x79, 0x12, 0x2f, 0x0a, 0x13, 0x65, 0x78, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, + 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, + 0x65, 0x78, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x6f, + 0x66, 0x66, 0x1a, 0x8e, 0x02, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, + 0x12, 0x3f, 0x0a, 0x02, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x67, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x4a, 0x6f, 0x62, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x02, 0x6f, + 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x08, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x69, 0x0a, + 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x51, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, + 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2e, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, + 0x69, 0x65, 0x72, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x39, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x22, 0x92, 0x01, 0x0a, 0x0d, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x70, 0x65, 0x6e, + 0x64, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x59, 0x0a, + 0x0f, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x44, 0x65, + 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x0e, 0x68, 0x74, 0x74, 0x70, 0x44, 0x65, + 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x22, 0xdc, 0x02, 0x0a, 0x0e, 0x48, 0x74, 0x74, + 0x70, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xde, 0x03, 0x0a, 0x08, 0x4a, 0x6f, 0x62, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x28, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, + 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, + 0x6c, 0x12, 0x57, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, + 0x65, 0x6e, 0x63, 0x79, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x54, 0x0a, 0x06, 0x70, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x67, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, + 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x48, 0x74, + 0x74, 0x70, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, + 0x1a, 0x3a, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x39, 0x0a, 0x0b, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x6a, 0x0a, 0x0b, 0x4a, 0x6f, 0x62, 0x53, 0x70, + 0x65, 0x63, 0x48, 0x6f, 0x6f, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x47, 0x0a, 0x06, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x67, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, + 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, + 0x62, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x22, 0x39, 0x0a, 0x0d, 0x4a, 0x6f, 0x62, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x49, 0x74, 0x65, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xe5, + 0x03, 0x0a, 0x08, 0x4a, 0x6f, 0x62, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x43, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x67, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, @@ -4080,582 +4545,703 @@ var file_odpf_optimus_core_v1beta1_job_spec_proto_rawDesc = []byte{ 0x54, 0x52, 0x59, 0x10, 0x11, 0x12, 0x12, 0x0a, 0x0e, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x48, 0x4f, 0x4f, 0x4b, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x10, 0x12, 0x12, 0x15, 0x0a, 0x11, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x48, 0x4f, 0x4f, 0x4b, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x13, - 0x22, 0x04, 0x08, 0x02, 0x10, 0x05, 0x22, 0xaa, 0x01, 0x0a, 0x0b, 0x4a, 0x6f, 0x62, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x4e, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, + 0x22, 0x04, 0x08, 0x02, 0x10, 0x05, 0x22, 0xb8, 0x01, 0x0a, 0x0b, 0x4a, 0x6f, 0x62, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x55, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, + 0x70, 0x65, 0x63, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x52, 0x0a, + 0x07, 0x61, 0x69, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x38, + 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x41, 0x69, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x07, 0x61, 0x69, 0x72, 0x66, 0x6c, 0x6f, + 0x77, 0x22, 0xcb, 0x01, 0x0a, 0x17, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x59, 0x0a, + 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3f, + 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, + 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x55, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, + 0x65, 0x63, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x22, + 0x49, 0x0a, 0x1d, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x12, 0x10, 0x0a, 0x03, 0x63, 0x70, 0x75, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x63, + 0x70, 0x75, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x22, 0x42, 0x0a, 0x16, 0x4a, 0x6f, + 0x62, 0x53, 0x70, 0x65, 0x63, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x41, 0x69, 0x72, + 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x70, 0x6f, 0x6f, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x75, 0x65, 0x22, 0x7d, + 0x0a, 0x12, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4a, 0x6f, 0x62, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, + 0x12, 0x1b, 0x0a, 0x09, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x08, 0x6a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x61, 0x0a, + 0x13, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4a, 0x6f, 0x62, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x5f, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x52, + 0x09, 0x6c, 0x6f, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x06, + 0x22, 0x39, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, 0x62, + 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, + 0x0a, 0x09, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x49, 0x64, 0x22, 0xa3, 0x03, 0x0a, 0x1b, + 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x4e, 0x0a, 0x08, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, + 0x6f, 0x62, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x08, 0x66, 0x61, 0x69, 0x6c, 0x75, + 0x72, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x73, 0x75, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x61, 0x69, 0x6c, + 0x75, 0x72, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x0c, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x89, 0x01, + 0x0a, 0x14, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x5f, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, + 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x56, 0x2e, 0x67, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x55, 0x6e, 0x6b, 0x6e, + 0x6f, 0x77, 0x6e, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x13, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x44, 0x65, 0x70, + 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x1a, 0x46, 0x0a, 0x18, 0x55, 0x6e, 0x6b, + 0x6e, 0x6f, 0x77, 0x6e, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0x47, 0x0a, 0x10, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, 0x62, 0x46, 0x61, + 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xb5, 0x01, 0x0a, 0x1b, 0x47, + 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x31, 0x0a, + 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x22, 0xe6, 0x01, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, + 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x4b, 0x0a, 0x07, 0x61, 0x69, 0x72, 0x66, 0x6c, 0x6f, - 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, - 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x41, 0x69, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x07, 0x61, 0x69, 0x72, 0x66, - 0x6c, 0x6f, 0x77, 0x22, 0xbd, 0x01, 0x0a, 0x17, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, - 0x52, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x38, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, - 0x53, 0x70, 0x65, 0x63, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x4e, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, - 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, - 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, 0x6c, 0x69, - 0x6d, 0x69, 0x74, 0x22, 0x49, 0x0a, 0x1d, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x70, 0x75, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x63, 0x70, 0x75, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x22, 0x42, - 0x0a, 0x16, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x41, 0x69, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x6f, 0x6c, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x6f, 0x6f, 0x6c, 0x12, 0x14, 0x0a, 0x05, - 0x71, 0x75, 0x65, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, - 0x75, 0x65, 0x22, 0x7d, 0x0a, 0x12, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4a, 0x6f, 0x62, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x02, 0x18, 0x01, 0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x12, + 0x7a, 0x0a, 0x1b, 0x6a, 0x6f, 0x62, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x52, 0x19, 0x6a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x22, 0xaa, 0x01, 0x0a, 0x18, + 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x6a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, - 0x73, 0x22, 0x5a, 0x0a, 0x13, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4a, 0x6f, 0x62, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x5f, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6f, - 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x09, 0x6c, 0x6f, - 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x06, 0x22, 0x39, 0x0a, - 0x1a, 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, - 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x49, 0x64, 0x22, 0x95, 0x03, 0x0a, 0x1b, 0x47, 0x65, 0x74, - 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x47, 0x0a, 0x08, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, - 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, - 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, 0x62, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, - 0x08, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x75, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x0c, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x23, - 0x0a, 0x0d, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x82, 0x01, 0x0a, 0x14, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x5f, - 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x4f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, - 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, - 0x77, 0x6e, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x13, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x44, 0x65, 0x70, 0x65, - 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x1a, 0x46, 0x0a, 0x18, 0x55, 0x6e, 0x6b, 0x6e, - 0x6f, 0x77, 0x6e, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x22, 0x47, 0x0a, 0x10, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, 0x62, 0x46, 0x61, 0x69, - 0x6c, 0x75, 0x72, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xb5, 0x01, 0x0a, 0x1b, 0x47, 0x65, - 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x31, 0x0a, 0x14, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, - 0x65, 0x22, 0xd8, 0x01, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x43, 0x0a, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x2b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, - 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x02, 0x18, - 0x01, 0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x12, 0x73, 0x0a, 0x1b, 0x6a, 0x6f, 0x62, 0x5f, 0x73, - 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x6f, - 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x52, 0x19, 0x6a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x22, 0xa3, 0x01, 0x0a, - 0x18, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x3d, 0x0a, 0x03, 0x6a, 0x6f, 0x62, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x2b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, - 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x6a, - 0x6f, 0x62, 0x22, 0xaf, 0x01, 0x0a, 0x22, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x41, 0x6c, - 0x6c, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x2b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, - 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, - 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, - 0x6a, 0x6f, 0x62, 0x73, 0x22, 0x64, 0x0a, 0x23, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x41, - 0x6c, 0x6c, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x0a, 0x6c, - 0x6f, 0x67, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x52, - 0x09, 0x6c, 0x6f, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x78, 0x0a, 0x11, 0x47, 0x65, - 0x74, 0x4a, 0x6f, 0x62, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x44, 0x0a, 0x03, 0x6a, 0x6f, 0x62, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x6a, 0x6f, 0x62, 0x22, 0xb6, 0x01, 0x0a, 0x22, 0x52, 0x65, 0x70, + 0x6c, 0x61, 0x63, 0x65, 0x41, 0x6c, 0x6c, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, - 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x4c, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x54, 0x61, - 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x04, 0x74, 0x61, - 0x73, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, - 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x04, 0x74, 0x61, - 0x73, 0x6b, 0x22, 0xed, 0x02, 0x0a, 0x07, 0x4a, 0x6f, 0x62, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x50, 0x0a, 0x0b, 0x64, 0x65, - 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x54, - 0x61, 0x73, 0x6b, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x51, 0x0a, 0x0c, - 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, - 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, - 0x6f, 0x62, 0x54, 0x61, 0x73, 0x6b, 0x2e, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, - 0x79, 0x52, 0x0c, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x1a, - 0x43, 0x0a, 0x0b, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, - 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x74, 0x79, 0x70, 0x65, 0x1a, 0x2c, 0x0a, 0x0a, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, - 0x63, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, - 0x63, 0x79, 0x22, 0xb8, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3d, 0x0a, 0x0c, 0x73, 0x63, 0x68, 0x65, 0x64, - 0x75, 0x6c, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x73, 0x63, 0x68, 0x65, 0x64, - 0x75, 0x6c, 0x65, 0x64, 0x41, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, - 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, - 0x65, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x74, - 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, - 0x65, 0x54, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x73, 0x0a, - 0x11, 0x47, 0x65, 0x74, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x30, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x05, 0x73, - 0x74, 0x61, 0x72, 0x74, 0x12, 0x2c, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x03, 0x65, - 0x6e, 0x64, 0x32, 0xe7, 0x16, 0x0a, 0x17, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x93, - 0x01, 0x0a, 0x16, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x2e, 0x6f, 0x64, 0x70, 0x66, - 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, 0x62, 0x53, - 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x46, 0x0a, 0x04, 0x6a, 0x6f, 0x62, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x70, + 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x6a, 0x6f, 0x62, + 0x73, 0x22, 0x6b, 0x0a, 0x23, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x41, 0x6c, 0x6c, 0x4a, + 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x5f, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x67, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x28, 0x01, 0x30, 0x01, 0x12, 0xbc, 0x01, 0x0a, 0x0a, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x73, 0x70, - 0x65, 0x63, 0x74, 0x12, 0x2c, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x4c, 0x6f, 0x67, 0x52, 0x09, 0x6c, 0x6f, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x78, + 0x0a, 0x11, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, + 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x53, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4a, + 0x6f, 0x62, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, + 0x0a, 0x04, 0x74, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x67, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, + 0x4a, 0x6f, 0x62, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x04, 0x74, 0x61, 0x73, 0x6b, 0x22, 0xfb, 0x02, + 0x0a, 0x07, 0x4a, 0x6f, 0x62, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x14, 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x57, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x67, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, - 0x62, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x51, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x4b, 0x22, 0x46, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x62, 0x54, 0x61, 0x73, 0x6b, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x58, + 0x0a, 0x0c, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x18, 0x05, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x54, 0x61, 0x73, 0x6b, 0x2e, + 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x0c, 0x64, 0x65, 0x70, 0x65, + 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x1a, 0x43, 0x0a, 0x0b, 0x44, 0x65, 0x73, 0x74, + 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, + 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, + 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x1a, 0x2c, 0x0a, + 0x0a, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x64, + 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x22, 0xb8, 0x01, 0x0a, 0x10, + 0x47, 0x65, 0x74, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x3d, 0x0a, 0x0c, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x5f, 0x61, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x52, 0x0b, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x41, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, + 0x69, 0x7a, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x74, + 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x54, 0x6f, 0x12, 0x18, 0x0a, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x73, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x57, 0x69, 0x6e, + 0x64, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x05, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2c, 0x0a, + 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0xd9, 0x01, 0x0a, 0x16, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x6d, 0x61, 0x72, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x72, 0x65, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x40, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2a, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6a, 0x6f, + 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x6a, + 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x19, 0x0a, 0x17, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x4a, 0x6f, 0x62, 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0xb1, 0x02, 0x0a, 0x14, 0x53, 0x79, 0x6e, 0x63, 0x4a, 0x6f, 0x62, 0x73, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, + 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x62, 0x0a, 0x0a, 0x6a, 0x6f, 0x62, 0x5f, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x43, 0x2e, 0x67, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, + 0x63, 0x4a, 0x6f, 0x62, 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x09, + 0x6a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x1a, 0x6b, 0x0a, 0x0c, 0x4a, 0x6f, 0x62, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x50, 0x61, 0x69, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x40, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x2a, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, + 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, + 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x17, 0x0a, 0x15, 0x53, 0x79, 0x6e, 0x63, 0x4a, 0x6f, + 0x62, 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, + 0x54, 0x0a, 0x08, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x15, 0x4a, + 0x4f, 0x42, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, + 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x4a, 0x4f, 0x42, 0x5f, 0x53, 0x54, + 0x41, 0x54, 0x45, 0x5f, 0x45, 0x4e, 0x41, 0x42, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x16, 0x0a, + 0x12, 0x4a, 0x4f, 0x42, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x44, 0x49, 0x53, 0x41, 0x42, + 0x4c, 0x45, 0x44, 0x10, 0x02, 0x32, 0xd5, 0x1d, 0x0a, 0x17, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, + 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x12, 0xa1, 0x01, 0x0a, 0x16, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, 0x62, 0x53, + 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3f, 0x2e, 0x67, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x40, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, + 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0xca, 0x01, 0x0a, 0x0a, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x73, + 0x70, 0x65, 0x63, 0x74, 0x12, 0x33, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x73, 0x70, 0x65, + 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x67, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, + 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x51, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x4b, 0x22, 0x46, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x2f, 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x3a, + 0x01, 0x2a, 0x12, 0xe6, 0x01, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, + 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3f, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, + 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x40, + 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x49, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x43, 0x22, 0x3e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x2f, 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, - 0x3a, 0x01, 0x2a, 0x12, 0xd8, 0x01, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4a, 0x6f, - 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, - 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, - 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, - 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x49, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x43, 0x22, 0x3e, 0x2f, 0x76, 0x31, + 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x3a, 0x01, 0x2a, 0x12, 0xe1, 0x01, 0x0a, 0x14, + 0x41, 0x64, 0x64, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x4a, 0x6f, 0x62, 0x53, 0x70, + 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x3e, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, + 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, + 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x4a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x44, 0x22, 0x3f, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x3a, 0x01, 0x2a, 0x12, 0xd3, - 0x01, 0x0a, 0x14, 0x41, 0x64, 0x64, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x36, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, - 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x37, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x4a, - 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x44, - 0x22, 0x3f, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, - 0x73, 0x3a, 0x01, 0x2a, 0x12, 0xdc, 0x01, 0x0a, 0x17, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, - 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x12, 0x39, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3a, 0x2e, 0x6f, 0x64, - 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, - 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x44, 0x1a, - 0x3f, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, - 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x73, - 0x3a, 0x01, 0x2a, 0x12, 0xd7, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, - 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x35, 0x2e, 0x6f, 0x64, - 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x73, 0x3a, 0x01, 0x2a, 0x12, + 0xea, 0x01, 0x0a, 0x17, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, + 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x40, 0x2e, 0x67, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, + 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x41, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, + 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x4a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x44, 0x1a, 0x3f, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0xe5, 0x01, 0x0a, + 0x13, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3c, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, - 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x51, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x4b, 0x12, 0x49, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6a, - 0x6f, 0x62, 0x2f, 0x7b, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0x9e, 0x01, - 0x0a, 0x14, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x36, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, - 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, - 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4a, 0x6f, - 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, - 0x0d, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x6a, 0x6f, 0x62, 0x73, 0x12, 0xe0, - 0x01, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x2e, 0x6f, 0x64, 0x70, 0x66, + 0x73, 0x74, 0x1a, 0x3d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, - 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x51, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x4b, 0x12, 0x49, 0x2f, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x2f, 0x7b, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x7d, 0x12, 0xac, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x53, + 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3d, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, + 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3e, 0x2e, 0x67, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x51, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x4b, 0x2a, 0x49, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x2f, 0x7b, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x7d, 0x12, 0xcf, 0x01, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, - 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x2e, 0x6f, 0x64, 0x70, - 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, - 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, - 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x46, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x40, 0x12, 0x3e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, - 0x6a, 0x6f, 0x62, 0x12, 0xbd, 0x01, 0x0a, 0x15, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4a, 0x6f, 0x62, - 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x2e, - 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4a, - 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, + 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x6a, + 0x6f, 0x62, 0x73, 0x12, 0xee, 0x01, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, + 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3f, + 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x40, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x22, 0x29, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x2f, 0x63, 0x68, - 0x65, 0x63, 0x6b, 0x12, 0x91, 0x01, 0x0a, 0x16, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4a, 0x6f, 0x62, - 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x38, - 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, + 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x51, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x4b, 0x2a, 0x49, 0x2f, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x2f, 0x7b, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x7d, 0x12, 0xd0, 0x01, 0x0a, 0x12, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4a, + 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x3b, 0x2e, 0x67, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, + 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3c, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x4a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x39, 0x22, 0x34, + 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2d, 0x6a, 0x6f, 0x62, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0xdd, 0x01, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, + 0x12, 0x3d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, + 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x3e, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x46, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x40, 0x12, 0x3e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x12, 0xcb, 0x01, 0x0a, 0x15, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x3e, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, - 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x70, 0x0a, 0x0b, 0x52, 0x65, 0x66, 0x72, 0x65, - 0x73, 0x68, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x2d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, - 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4a, 0x6f, 0x62, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, - 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4a, 0x6f, 0x62, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x86, 0x01, 0x0a, 0x13, 0x47, 0x65, - 0x74, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x35, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, - 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, + 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x3f, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, - 0x62, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0xa2, 0x01, 0x0a, 0x1b, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x41, 0x6c, - 0x6c, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x12, 0x3d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, - 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, - 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x41, 0x6c, 0x6c, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, + 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x22, 0x29, 0x2f, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x2f, + 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x9f, 0x01, 0x0a, 0x16, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4a, + 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x3f, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, + 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x3e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, - 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, - 0x70, 0x6c, 0x61, 0x63, 0x65, 0x41, 0x6c, 0x6c, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0xc1, 0x01, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x4a, - 0x6f, 0x62, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x2c, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, + 0x74, 0x1a, 0x40, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, + 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, + 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x7e, 0x0a, 0x0b, 0x52, 0x65, 0x66, 0x72, 0x65, + 0x73, 0x68, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x34, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, + 0x68, 0x4a, 0x6f, 0x62, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x67, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4a, 0x6f, 0x62, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x94, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x44, + 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x3c, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, 0x69, + 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, 0x62, 0x73, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3d, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x56, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x50, 0x12, 0x4e, 0x2f, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6a, 0x6f, 0x62, 0x2f, 0x7b, 0x6a, 0x6f, 0x62, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x74, 0x61, 0x73, 0x6b, 0x12, 0x7f, 0x0a, 0x09, 0x47, - 0x65, 0x74, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, 0x2b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, + 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0xb0, + 0x01, 0x0a, 0x1b, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x41, 0x6c, 0x6c, 0x4a, 0x6f, 0x62, + 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x44, + 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x41, 0x6c, 0x6c, 0x4a, 0x6f, 0x62, 0x53, + 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x45, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x41, + 0x6c, 0x6c, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, + 0x01, 0x12, 0xcf, 0x01, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x54, 0x61, 0x73, 0x6b, + 0x12, 0x33, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, + 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x54, + 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x56, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x50, 0x12, 0x4e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, + 0x6a, 0x6f, 0x62, 0x2f, 0x7b, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x74, + 0x61, 0x73, 0x6b, 0x12, 0x8d, 0x01, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x57, 0x69, 0x6e, 0x64, 0x6f, + 0x77, 0x12, 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x6f, 0x70, 0x74, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x69, 0x6e, 0x64, + 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x77, 0x69, 0x6e, + 0x64, 0x6f, 0x77, 0x12, 0xde, 0x01, 0x0a, 0x0f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, + 0x62, 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x38, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x4a, 0x6f, 0x62, 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x39, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, + 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x73, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x56, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x50, 0x32, 0x4b, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, + 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, + 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2d, 0x6a, 0x6f, 0x62, 0x2d, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x3a, 0x01, 0x2a, 0x12, 0xd6, 0x01, 0x0a, 0x0d, 0x53, 0x79, 0x6e, 0x63, 0x4a, 0x6f, 0x62, + 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x4a, 0x6f, + 0x62, 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, + 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x42, 0xa2, 0x01, 0x0a, - 0x16, 0x69, 0x6f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2e, - 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x42, 0x1e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x1e, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x6e, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x92, 0x41, 0x45, 0x12, 0x05, 0x32, 0x03, - 0x30, 0x2e, 0x31, 0x1a, 0x0e, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x3a, 0x39, - 0x31, 0x30, 0x30, 0x22, 0x04, 0x2f, 0x61, 0x70, 0x69, 0x2a, 0x01, 0x01, 0x72, 0x23, 0x0a, 0x21, - 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x20, 0x4a, 0x6f, 0x62, 0x20, 0x53, 0x70, 0x65, 0x63, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x4a, 0x6f, 0x62, 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x54, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x4e, 0x32, + 0x49, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, + 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x73, 0x79, 0x6e, 0x63, + 0x2d, 0x6a, 0x6f, 0x62, 0x2d, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x42, 0xaa, 0x01, + 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, + 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, + 0x42, 0x1e, 0x4a, 0x6f, 0x62, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, + 0x50, 0x01, 0x5a, 0x1e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, + 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x75, 0x73, 0x92, 0x41, 0x45, 0x12, 0x05, 0x32, 0x03, 0x30, 0x2e, 0x31, 0x1a, 0x0e, 0x31, 0x32, + 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x3a, 0x39, 0x31, 0x30, 0x30, 0x22, 0x04, 0x2f, 0x61, + 0x70, 0x69, 0x2a, 0x01, 0x01, 0x72, 0x23, 0x0a, 0x21, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, + 0x20, 0x4a, 0x6f, 0x62, 0x20, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( - file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescOnce sync.Once - file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescData = file_odpf_optimus_core_v1beta1_job_spec_proto_rawDesc + file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescOnce sync.Once + file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescData = file_raystack_optimus_core_v1beta1_job_spec_proto_rawDesc ) -func file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescGZIP() []byte { - file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescOnce.Do(func() { - file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescData = protoimpl.X.CompressGZIP(file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescData) +func file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescGZIP() []byte { + file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescOnce.Do(func() { + file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescData = protoimpl.X.CompressGZIP(file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescData) }) - return file_odpf_optimus_core_v1beta1_job_spec_proto_rawDescData -} - -var file_odpf_optimus_core_v1beta1_job_spec_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes = make([]protoimpl.MessageInfo, 62) -var file_odpf_optimus_core_v1beta1_job_spec_proto_goTypes = []interface{}{ - (JobEvent_Type)(0), // 0: odpf.optimus.core.v1beta1.JobEvent.Type - (*DeployJobSpecificationRequest)(nil), // 1: odpf.optimus.core.v1beta1.DeployJobSpecificationRequest - (*DeployJobSpecificationResponse)(nil), // 2: odpf.optimus.core.v1beta1.DeployJobSpecificationResponse - (*AddJobSpecificationsRequest)(nil), // 3: odpf.optimus.core.v1beta1.AddJobSpecificationsRequest - (*AddJobSpecificationsResponse)(nil), // 4: odpf.optimus.core.v1beta1.AddJobSpecificationsResponse - (*UpdateJobSpecificationsRequest)(nil), // 5: odpf.optimus.core.v1beta1.UpdateJobSpecificationsRequest - (*UpdateJobSpecificationsResponse)(nil), // 6: odpf.optimus.core.v1beta1.UpdateJobSpecificationsResponse - (*JobInspectRequest)(nil), // 7: odpf.optimus.core.v1beta1.JobInspectRequest - (*JobRun)(nil), // 8: odpf.optimus.core.v1beta1.JobRun - (*JobInspectResponse)(nil), // 9: odpf.optimus.core.v1beta1.JobInspectResponse - (*CreateJobSpecificationRequest)(nil), // 10: odpf.optimus.core.v1beta1.CreateJobSpecificationRequest - (*CreateJobSpecificationResponse)(nil), // 11: odpf.optimus.core.v1beta1.CreateJobSpecificationResponse - (*GetJobSpecificationRequest)(nil), // 12: odpf.optimus.core.v1beta1.GetJobSpecificationRequest - (*GetJobSpecificationResponse)(nil), // 13: odpf.optimus.core.v1beta1.GetJobSpecificationResponse - (*DeleteJobSpecificationRequest)(nil), // 14: odpf.optimus.core.v1beta1.DeleteJobSpecificationRequest - (*DeleteJobSpecificationResponse)(nil), // 15: odpf.optimus.core.v1beta1.DeleteJobSpecificationResponse - (*ListJobSpecificationRequest)(nil), // 16: odpf.optimus.core.v1beta1.ListJobSpecificationRequest - (*ListJobSpecificationResponse)(nil), // 17: odpf.optimus.core.v1beta1.ListJobSpecificationResponse - (*CheckJobSpecificationRequest)(nil), // 18: odpf.optimus.core.v1beta1.CheckJobSpecificationRequest - (*CheckJobSpecificationResponse)(nil), // 19: odpf.optimus.core.v1beta1.CheckJobSpecificationResponse - (*CheckJobSpecificationsRequest)(nil), // 20: odpf.optimus.core.v1beta1.CheckJobSpecificationsRequest - (*CheckJobSpecificationsResponse)(nil), // 21: odpf.optimus.core.v1beta1.CheckJobSpecificationsResponse - (*JobSpecification)(nil), // 22: odpf.optimus.core.v1beta1.JobSpecification - (*JobDependency)(nil), // 23: odpf.optimus.core.v1beta1.JobDependency - (*HttpDependency)(nil), // 24: odpf.optimus.core.v1beta1.HttpDependency - (*JobSpecHook)(nil), // 25: odpf.optimus.core.v1beta1.JobSpecHook - (*JobConfigItem)(nil), // 26: odpf.optimus.core.v1beta1.JobConfigItem - (*JobEvent)(nil), // 27: odpf.optimus.core.v1beta1.JobEvent - (*JobMetadata)(nil), // 28: odpf.optimus.core.v1beta1.JobMetadata - (*JobSpecMetadataResource)(nil), // 29: odpf.optimus.core.v1beta1.JobSpecMetadataResource - (*JobSpecMetadataResourceConfig)(nil), // 30: odpf.optimus.core.v1beta1.JobSpecMetadataResourceConfig - (*JobSpecMetadataAirflow)(nil), // 31: odpf.optimus.core.v1beta1.JobSpecMetadataAirflow - (*RefreshJobsRequest)(nil), // 32: odpf.optimus.core.v1beta1.RefreshJobsRequest - (*RefreshJobsResponse)(nil), // 33: odpf.optimus.core.v1beta1.RefreshJobsResponse - (*GetDeployJobsStatusRequest)(nil), // 34: odpf.optimus.core.v1beta1.GetDeployJobsStatusRequest - (*GetDeployJobsStatusResponse)(nil), // 35: odpf.optimus.core.v1beta1.GetDeployJobsStatusResponse - (*DeployJobFailure)(nil), // 36: odpf.optimus.core.v1beta1.DeployJobFailure - (*GetJobSpecificationsRequest)(nil), // 37: odpf.optimus.core.v1beta1.GetJobSpecificationsRequest - (*GetJobSpecificationsResponse)(nil), // 38: odpf.optimus.core.v1beta1.GetJobSpecificationsResponse - (*JobSpecificationResponse)(nil), // 39: odpf.optimus.core.v1beta1.JobSpecificationResponse - (*ReplaceAllJobSpecificationsRequest)(nil), // 40: odpf.optimus.core.v1beta1.ReplaceAllJobSpecificationsRequest - (*ReplaceAllJobSpecificationsResponse)(nil), // 41: odpf.optimus.core.v1beta1.ReplaceAllJobSpecificationsResponse - (*GetJobTaskRequest)(nil), // 42: odpf.optimus.core.v1beta1.GetJobTaskRequest - (*GetJobTaskResponse)(nil), // 43: odpf.optimus.core.v1beta1.GetJobTaskResponse - (*JobTask)(nil), // 44: odpf.optimus.core.v1beta1.JobTask - (*GetWindowRequest)(nil), // 45: odpf.optimus.core.v1beta1.GetWindowRequest - (*GetWindowResponse)(nil), // 46: odpf.optimus.core.v1beta1.GetWindowResponse - (*JobInspectResponse_BasicInfoSection)(nil), // 47: odpf.optimus.core.v1beta1.JobInspectResponse.BasicInfoSection - (*JobInspectResponse_JobDependency)(nil), // 48: odpf.optimus.core.v1beta1.JobInspectResponse.JobDependency - (*JobInspectResponse_UpstreamSection)(nil), // 49: odpf.optimus.core.v1beta1.JobInspectResponse.UpstreamSection - (*JobInspectResponse_DownstreamSection)(nil), // 50: odpf.optimus.core.v1beta1.JobInspectResponse.DownstreamSection - (*JobInspectResponse_UpstreamSection_UnknownDependencies)(nil), // 51: odpf.optimus.core.v1beta1.JobInspectResponse.UpstreamSection.UnknownDependencies - nil, // 52: odpf.optimus.core.v1beta1.JobSpecification.AssetsEntry - nil, // 53: odpf.optimus.core.v1beta1.JobSpecification.LabelsEntry - (*JobSpecification_Behavior)(nil), // 54: odpf.optimus.core.v1beta1.JobSpecification.Behavior - (*JobSpecification_Behavior_Retry)(nil), // 55: odpf.optimus.core.v1beta1.JobSpecification.Behavior.Retry - (*JobSpecification_Behavior_Notifiers)(nil), // 56: odpf.optimus.core.v1beta1.JobSpecification.Behavior.Notifiers - nil, // 57: odpf.optimus.core.v1beta1.JobSpecification.Behavior.Notifiers.ConfigEntry - nil, // 58: odpf.optimus.core.v1beta1.HttpDependency.HeadersEntry - nil, // 59: odpf.optimus.core.v1beta1.HttpDependency.ParamsEntry - nil, // 60: odpf.optimus.core.v1beta1.GetDeployJobsStatusResponse.UnknownDependenciesEntry - (*JobTask_Destination)(nil), // 61: odpf.optimus.core.v1beta1.JobTask.Destination - (*JobTask_Dependency)(nil), // 62: odpf.optimus.core.v1beta1.JobTask.Dependency - (*Log)(nil), // 63: odpf.optimus.core.v1beta1.Log - (*timestamppb.Timestamp)(nil), // 64: google.protobuf.Timestamp - (*structpb.Struct)(nil), // 65: google.protobuf.Struct - (*durationpb.Duration)(nil), // 66: google.protobuf.Duration -} -var file_odpf_optimus_core_v1beta1_job_spec_proto_depIdxs = []int32{ - 22, // 0: odpf.optimus.core.v1beta1.DeployJobSpecificationRequest.jobs:type_name -> odpf.optimus.core.v1beta1.JobSpecification - 63, // 1: odpf.optimus.core.v1beta1.DeployJobSpecificationResponse.log_status:type_name -> odpf.optimus.core.v1beta1.Log - 22, // 2: odpf.optimus.core.v1beta1.AddJobSpecificationsRequest.specs:type_name -> odpf.optimus.core.v1beta1.JobSpecification - 22, // 3: odpf.optimus.core.v1beta1.UpdateJobSpecificationsRequest.specs:type_name -> odpf.optimus.core.v1beta1.JobSpecification - 22, // 4: odpf.optimus.core.v1beta1.JobInspectRequest.spec:type_name -> odpf.optimus.core.v1beta1.JobSpecification - 64, // 5: odpf.optimus.core.v1beta1.JobInspectRequest.scheduled_at:type_name -> google.protobuf.Timestamp - 64, // 6: odpf.optimus.core.v1beta1.JobRun.scheduled_at:type_name -> google.protobuf.Timestamp - 47, // 7: odpf.optimus.core.v1beta1.JobInspectResponse.basic_info:type_name -> odpf.optimus.core.v1beta1.JobInspectResponse.BasicInfoSection - 49, // 8: odpf.optimus.core.v1beta1.JobInspectResponse.upstreams:type_name -> odpf.optimus.core.v1beta1.JobInspectResponse.UpstreamSection - 50, // 9: odpf.optimus.core.v1beta1.JobInspectResponse.downstreams:type_name -> odpf.optimus.core.v1beta1.JobInspectResponse.DownstreamSection - 22, // 10: odpf.optimus.core.v1beta1.CreateJobSpecificationRequest.spec:type_name -> odpf.optimus.core.v1beta1.JobSpecification - 22, // 11: odpf.optimus.core.v1beta1.GetJobSpecificationResponse.spec:type_name -> odpf.optimus.core.v1beta1.JobSpecification - 22, // 12: odpf.optimus.core.v1beta1.ListJobSpecificationResponse.jobs:type_name -> odpf.optimus.core.v1beta1.JobSpecification - 22, // 13: odpf.optimus.core.v1beta1.CheckJobSpecificationRequest.job:type_name -> odpf.optimus.core.v1beta1.JobSpecification - 22, // 14: odpf.optimus.core.v1beta1.CheckJobSpecificationsRequest.jobs:type_name -> odpf.optimus.core.v1beta1.JobSpecification - 63, // 15: odpf.optimus.core.v1beta1.CheckJobSpecificationsResponse.log_status:type_name -> odpf.optimus.core.v1beta1.Log - 26, // 16: odpf.optimus.core.v1beta1.JobSpecification.config:type_name -> odpf.optimus.core.v1beta1.JobConfigItem - 23, // 17: odpf.optimus.core.v1beta1.JobSpecification.dependencies:type_name -> odpf.optimus.core.v1beta1.JobDependency - 52, // 18: odpf.optimus.core.v1beta1.JobSpecification.assets:type_name -> odpf.optimus.core.v1beta1.JobSpecification.AssetsEntry - 25, // 19: odpf.optimus.core.v1beta1.JobSpecification.hooks:type_name -> odpf.optimus.core.v1beta1.JobSpecHook - 53, // 20: odpf.optimus.core.v1beta1.JobSpecification.labels:type_name -> odpf.optimus.core.v1beta1.JobSpecification.LabelsEntry - 54, // 21: odpf.optimus.core.v1beta1.JobSpecification.behavior:type_name -> odpf.optimus.core.v1beta1.JobSpecification.Behavior - 28, // 22: odpf.optimus.core.v1beta1.JobSpecification.metadata:type_name -> odpf.optimus.core.v1beta1.JobMetadata - 24, // 23: odpf.optimus.core.v1beta1.JobDependency.http_dependency:type_name -> odpf.optimus.core.v1beta1.HttpDependency - 58, // 24: odpf.optimus.core.v1beta1.HttpDependency.headers:type_name -> odpf.optimus.core.v1beta1.HttpDependency.HeadersEntry - 59, // 25: odpf.optimus.core.v1beta1.HttpDependency.params:type_name -> odpf.optimus.core.v1beta1.HttpDependency.ParamsEntry - 26, // 26: odpf.optimus.core.v1beta1.JobSpecHook.config:type_name -> odpf.optimus.core.v1beta1.JobConfigItem - 0, // 27: odpf.optimus.core.v1beta1.JobEvent.type:type_name -> odpf.optimus.core.v1beta1.JobEvent.Type - 65, // 28: odpf.optimus.core.v1beta1.JobEvent.value:type_name -> google.protobuf.Struct - 29, // 29: odpf.optimus.core.v1beta1.JobMetadata.resource:type_name -> odpf.optimus.core.v1beta1.JobSpecMetadataResource - 31, // 30: odpf.optimus.core.v1beta1.JobMetadata.airflow:type_name -> odpf.optimus.core.v1beta1.JobSpecMetadataAirflow - 30, // 31: odpf.optimus.core.v1beta1.JobSpecMetadataResource.request:type_name -> odpf.optimus.core.v1beta1.JobSpecMetadataResourceConfig - 30, // 32: odpf.optimus.core.v1beta1.JobSpecMetadataResource.limit:type_name -> odpf.optimus.core.v1beta1.JobSpecMetadataResourceConfig - 63, // 33: odpf.optimus.core.v1beta1.RefreshJobsResponse.log_status:type_name -> odpf.optimus.core.v1beta1.Log - 36, // 34: odpf.optimus.core.v1beta1.GetDeployJobsStatusResponse.failures:type_name -> odpf.optimus.core.v1beta1.DeployJobFailure - 60, // 35: odpf.optimus.core.v1beta1.GetDeployJobsStatusResponse.unknown_dependencies:type_name -> odpf.optimus.core.v1beta1.GetDeployJobsStatusResponse.UnknownDependenciesEntry - 22, // 36: odpf.optimus.core.v1beta1.GetJobSpecificationsResponse.jobs:type_name -> odpf.optimus.core.v1beta1.JobSpecification - 39, // 37: odpf.optimus.core.v1beta1.GetJobSpecificationsResponse.job_specification_responses:type_name -> odpf.optimus.core.v1beta1.JobSpecificationResponse - 22, // 38: odpf.optimus.core.v1beta1.JobSpecificationResponse.job:type_name -> odpf.optimus.core.v1beta1.JobSpecification - 22, // 39: odpf.optimus.core.v1beta1.ReplaceAllJobSpecificationsRequest.jobs:type_name -> odpf.optimus.core.v1beta1.JobSpecification - 63, // 40: odpf.optimus.core.v1beta1.ReplaceAllJobSpecificationsResponse.log_status:type_name -> odpf.optimus.core.v1beta1.Log - 44, // 41: odpf.optimus.core.v1beta1.GetJobTaskResponse.task:type_name -> odpf.optimus.core.v1beta1.JobTask - 61, // 42: odpf.optimus.core.v1beta1.JobTask.destination:type_name -> odpf.optimus.core.v1beta1.JobTask.Destination - 62, // 43: odpf.optimus.core.v1beta1.JobTask.dependencies:type_name -> odpf.optimus.core.v1beta1.JobTask.Dependency - 64, // 44: odpf.optimus.core.v1beta1.GetWindowRequest.scheduled_at:type_name -> google.protobuf.Timestamp - 64, // 45: odpf.optimus.core.v1beta1.GetWindowResponse.start:type_name -> google.protobuf.Timestamp - 64, // 46: odpf.optimus.core.v1beta1.GetWindowResponse.end:type_name -> google.protobuf.Timestamp - 22, // 47: odpf.optimus.core.v1beta1.JobInspectResponse.BasicInfoSection.job:type_name -> odpf.optimus.core.v1beta1.JobSpecification - 63, // 48: odpf.optimus.core.v1beta1.JobInspectResponse.BasicInfoSection.notice:type_name -> odpf.optimus.core.v1beta1.Log - 8, // 49: odpf.optimus.core.v1beta1.JobInspectResponse.JobDependency.runs:type_name -> odpf.optimus.core.v1beta1.JobRun - 48, // 50: odpf.optimus.core.v1beta1.JobInspectResponse.UpstreamSection.external_dependency:type_name -> odpf.optimus.core.v1beta1.JobInspectResponse.JobDependency - 48, // 51: odpf.optimus.core.v1beta1.JobInspectResponse.UpstreamSection.internal_dependency:type_name -> odpf.optimus.core.v1beta1.JobInspectResponse.JobDependency - 24, // 52: odpf.optimus.core.v1beta1.JobInspectResponse.UpstreamSection.http_dependency:type_name -> odpf.optimus.core.v1beta1.HttpDependency - 51, // 53: odpf.optimus.core.v1beta1.JobInspectResponse.UpstreamSection.unknown_dependencies:type_name -> odpf.optimus.core.v1beta1.JobInspectResponse.UpstreamSection.UnknownDependencies - 63, // 54: odpf.optimus.core.v1beta1.JobInspectResponse.UpstreamSection.notice:type_name -> odpf.optimus.core.v1beta1.Log - 48, // 55: odpf.optimus.core.v1beta1.JobInspectResponse.DownstreamSection.downstream_jobs:type_name -> odpf.optimus.core.v1beta1.JobInspectResponse.JobDependency - 63, // 56: odpf.optimus.core.v1beta1.JobInspectResponse.DownstreamSection.notice:type_name -> odpf.optimus.core.v1beta1.Log - 55, // 57: odpf.optimus.core.v1beta1.JobSpecification.Behavior.retry:type_name -> odpf.optimus.core.v1beta1.JobSpecification.Behavior.Retry - 56, // 58: odpf.optimus.core.v1beta1.JobSpecification.Behavior.notify:type_name -> odpf.optimus.core.v1beta1.JobSpecification.Behavior.Notifiers - 66, // 59: odpf.optimus.core.v1beta1.JobSpecification.Behavior.Retry.delay:type_name -> google.protobuf.Duration - 0, // 60: odpf.optimus.core.v1beta1.JobSpecification.Behavior.Notifiers.on:type_name -> odpf.optimus.core.v1beta1.JobEvent.Type - 57, // 61: odpf.optimus.core.v1beta1.JobSpecification.Behavior.Notifiers.config:type_name -> odpf.optimus.core.v1beta1.JobSpecification.Behavior.Notifiers.ConfigEntry - 1, // 62: odpf.optimus.core.v1beta1.JobSpecificationService.DeployJobSpecification:input_type -> odpf.optimus.core.v1beta1.DeployJobSpecificationRequest - 7, // 63: odpf.optimus.core.v1beta1.JobSpecificationService.JobInspect:input_type -> odpf.optimus.core.v1beta1.JobInspectRequest - 10, // 64: odpf.optimus.core.v1beta1.JobSpecificationService.CreateJobSpecification:input_type -> odpf.optimus.core.v1beta1.CreateJobSpecificationRequest - 3, // 65: odpf.optimus.core.v1beta1.JobSpecificationService.AddJobSpecifications:input_type -> odpf.optimus.core.v1beta1.AddJobSpecificationsRequest - 5, // 66: odpf.optimus.core.v1beta1.JobSpecificationService.UpdateJobSpecifications:input_type -> odpf.optimus.core.v1beta1.UpdateJobSpecificationsRequest - 12, // 67: odpf.optimus.core.v1beta1.JobSpecificationService.GetJobSpecification:input_type -> odpf.optimus.core.v1beta1.GetJobSpecificationRequest - 37, // 68: odpf.optimus.core.v1beta1.JobSpecificationService.GetJobSpecifications:input_type -> odpf.optimus.core.v1beta1.GetJobSpecificationsRequest - 14, // 69: odpf.optimus.core.v1beta1.JobSpecificationService.DeleteJobSpecification:input_type -> odpf.optimus.core.v1beta1.DeleteJobSpecificationRequest - 16, // 70: odpf.optimus.core.v1beta1.JobSpecificationService.ListJobSpecification:input_type -> odpf.optimus.core.v1beta1.ListJobSpecificationRequest - 18, // 71: odpf.optimus.core.v1beta1.JobSpecificationService.CheckJobSpecification:input_type -> odpf.optimus.core.v1beta1.CheckJobSpecificationRequest - 20, // 72: odpf.optimus.core.v1beta1.JobSpecificationService.CheckJobSpecifications:input_type -> odpf.optimus.core.v1beta1.CheckJobSpecificationsRequest - 32, // 73: odpf.optimus.core.v1beta1.JobSpecificationService.RefreshJobs:input_type -> odpf.optimus.core.v1beta1.RefreshJobsRequest - 34, // 74: odpf.optimus.core.v1beta1.JobSpecificationService.GetDeployJobsStatus:input_type -> odpf.optimus.core.v1beta1.GetDeployJobsStatusRequest - 40, // 75: odpf.optimus.core.v1beta1.JobSpecificationService.ReplaceAllJobSpecifications:input_type -> odpf.optimus.core.v1beta1.ReplaceAllJobSpecificationsRequest - 42, // 76: odpf.optimus.core.v1beta1.JobSpecificationService.GetJobTask:input_type -> odpf.optimus.core.v1beta1.GetJobTaskRequest - 45, // 77: odpf.optimus.core.v1beta1.JobSpecificationService.GetWindow:input_type -> odpf.optimus.core.v1beta1.GetWindowRequest - 2, // 78: odpf.optimus.core.v1beta1.JobSpecificationService.DeployJobSpecification:output_type -> odpf.optimus.core.v1beta1.DeployJobSpecificationResponse - 9, // 79: odpf.optimus.core.v1beta1.JobSpecificationService.JobInspect:output_type -> odpf.optimus.core.v1beta1.JobInspectResponse - 11, // 80: odpf.optimus.core.v1beta1.JobSpecificationService.CreateJobSpecification:output_type -> odpf.optimus.core.v1beta1.CreateJobSpecificationResponse - 4, // 81: odpf.optimus.core.v1beta1.JobSpecificationService.AddJobSpecifications:output_type -> odpf.optimus.core.v1beta1.AddJobSpecificationsResponse - 6, // 82: odpf.optimus.core.v1beta1.JobSpecificationService.UpdateJobSpecifications:output_type -> odpf.optimus.core.v1beta1.UpdateJobSpecificationsResponse - 13, // 83: odpf.optimus.core.v1beta1.JobSpecificationService.GetJobSpecification:output_type -> odpf.optimus.core.v1beta1.GetJobSpecificationResponse - 38, // 84: odpf.optimus.core.v1beta1.JobSpecificationService.GetJobSpecifications:output_type -> odpf.optimus.core.v1beta1.GetJobSpecificationsResponse - 15, // 85: odpf.optimus.core.v1beta1.JobSpecificationService.DeleteJobSpecification:output_type -> odpf.optimus.core.v1beta1.DeleteJobSpecificationResponse - 17, // 86: odpf.optimus.core.v1beta1.JobSpecificationService.ListJobSpecification:output_type -> odpf.optimus.core.v1beta1.ListJobSpecificationResponse - 19, // 87: odpf.optimus.core.v1beta1.JobSpecificationService.CheckJobSpecification:output_type -> odpf.optimus.core.v1beta1.CheckJobSpecificationResponse - 21, // 88: odpf.optimus.core.v1beta1.JobSpecificationService.CheckJobSpecifications:output_type -> odpf.optimus.core.v1beta1.CheckJobSpecificationsResponse - 33, // 89: odpf.optimus.core.v1beta1.JobSpecificationService.RefreshJobs:output_type -> odpf.optimus.core.v1beta1.RefreshJobsResponse - 35, // 90: odpf.optimus.core.v1beta1.JobSpecificationService.GetDeployJobsStatus:output_type -> odpf.optimus.core.v1beta1.GetDeployJobsStatusResponse - 41, // 91: odpf.optimus.core.v1beta1.JobSpecificationService.ReplaceAllJobSpecifications:output_type -> odpf.optimus.core.v1beta1.ReplaceAllJobSpecificationsResponse - 43, // 92: odpf.optimus.core.v1beta1.JobSpecificationService.GetJobTask:output_type -> odpf.optimus.core.v1beta1.GetJobTaskResponse - 46, // 93: odpf.optimus.core.v1beta1.JobSpecificationService.GetWindow:output_type -> odpf.optimus.core.v1beta1.GetWindowResponse - 78, // [78:94] is the sub-list for method output_type - 62, // [62:78] is the sub-list for method input_type - 62, // [62:62] is the sub-list for extension type_name - 62, // [62:62] is the sub-list for extension extendee - 0, // [0:62] is the sub-list for field type_name -} - -func init() { file_odpf_optimus_core_v1beta1_job_spec_proto_init() } -func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { - if File_odpf_optimus_core_v1beta1_job_spec_proto != nil { + return file_raystack_optimus_core_v1beta1_job_spec_proto_rawDescData +} + +var file_raystack_optimus_core_v1beta1_job_spec_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes = make([]protoimpl.MessageInfo, 69) +var file_raystack_optimus_core_v1beta1_job_spec_proto_goTypes = []interface{}{ + (JobState)(0), // 0: raystack.optimus.core.v1beta1.JobState + (JobEvent_Type)(0), // 1: raystack.optimus.core.v1beta1.JobEvent.Type + (*DeployJobSpecificationRequest)(nil), // 2: raystack.optimus.core.v1beta1.DeployJobSpecificationRequest + (*DeployJobSpecificationResponse)(nil), // 3: raystack.optimus.core.v1beta1.DeployJobSpecificationResponse + (*AddJobSpecificationsRequest)(nil), // 4: raystack.optimus.core.v1beta1.AddJobSpecificationsRequest + (*AddJobSpecificationsResponse)(nil), // 5: raystack.optimus.core.v1beta1.AddJobSpecificationsResponse + (*UpdateJobSpecificationsRequest)(nil), // 6: raystack.optimus.core.v1beta1.UpdateJobSpecificationsRequest + (*UpdateJobSpecificationsResponse)(nil), // 7: raystack.optimus.core.v1beta1.UpdateJobSpecificationsResponse + (*JobInspectRequest)(nil), // 8: raystack.optimus.core.v1beta1.JobInspectRequest + (*JobRun)(nil), // 9: raystack.optimus.core.v1beta1.JobRun + (*JobInspectResponse)(nil), // 10: raystack.optimus.core.v1beta1.JobInspectResponse + (*CreateJobSpecificationRequest)(nil), // 11: raystack.optimus.core.v1beta1.CreateJobSpecificationRequest + (*CreateJobSpecificationResponse)(nil), // 12: raystack.optimus.core.v1beta1.CreateJobSpecificationResponse + (*GetJobSpecificationRequest)(nil), // 13: raystack.optimus.core.v1beta1.GetJobSpecificationRequest + (*GetJobSpecificationResponse)(nil), // 14: raystack.optimus.core.v1beta1.GetJobSpecificationResponse + (*DeleteJobSpecificationRequest)(nil), // 15: raystack.optimus.core.v1beta1.DeleteJobSpecificationRequest + (*DeleteJobSpecificationResponse)(nil), // 16: raystack.optimus.core.v1beta1.DeleteJobSpecificationResponse + (*ChangeJobNamespaceRequest)(nil), // 17: raystack.optimus.core.v1beta1.ChangeJobNamespaceRequest + (*ChangeJobNamespaceResponse)(nil), // 18: raystack.optimus.core.v1beta1.ChangeJobNamespaceResponse + (*ListJobSpecificationRequest)(nil), // 19: raystack.optimus.core.v1beta1.ListJobSpecificationRequest + (*ListJobSpecificationResponse)(nil), // 20: raystack.optimus.core.v1beta1.ListJobSpecificationResponse + (*CheckJobSpecificationRequest)(nil), // 21: raystack.optimus.core.v1beta1.CheckJobSpecificationRequest + (*CheckJobSpecificationResponse)(nil), // 22: raystack.optimus.core.v1beta1.CheckJobSpecificationResponse + (*CheckJobSpecificationsRequest)(nil), // 23: raystack.optimus.core.v1beta1.CheckJobSpecificationsRequest + (*CheckJobSpecificationsResponse)(nil), // 24: raystack.optimus.core.v1beta1.CheckJobSpecificationsResponse + (*JobSpecification)(nil), // 25: raystack.optimus.core.v1beta1.JobSpecification + (*JobDependency)(nil), // 26: raystack.optimus.core.v1beta1.JobDependency + (*HttpDependency)(nil), // 27: raystack.optimus.core.v1beta1.HttpDependency + (*JobSpecHook)(nil), // 28: raystack.optimus.core.v1beta1.JobSpecHook + (*JobConfigItem)(nil), // 29: raystack.optimus.core.v1beta1.JobConfigItem + (*JobEvent)(nil), // 30: raystack.optimus.core.v1beta1.JobEvent + (*JobMetadata)(nil), // 31: raystack.optimus.core.v1beta1.JobMetadata + (*JobSpecMetadataResource)(nil), // 32: raystack.optimus.core.v1beta1.JobSpecMetadataResource + (*JobSpecMetadataResourceConfig)(nil), // 33: raystack.optimus.core.v1beta1.JobSpecMetadataResourceConfig + (*JobSpecMetadataAirflow)(nil), // 34: raystack.optimus.core.v1beta1.JobSpecMetadataAirflow + (*RefreshJobsRequest)(nil), // 35: raystack.optimus.core.v1beta1.RefreshJobsRequest + (*RefreshJobsResponse)(nil), // 36: raystack.optimus.core.v1beta1.RefreshJobsResponse + (*GetDeployJobsStatusRequest)(nil), // 37: raystack.optimus.core.v1beta1.GetDeployJobsStatusRequest + (*GetDeployJobsStatusResponse)(nil), // 38: raystack.optimus.core.v1beta1.GetDeployJobsStatusResponse + (*DeployJobFailure)(nil), // 39: raystack.optimus.core.v1beta1.DeployJobFailure + (*GetJobSpecificationsRequest)(nil), // 40: raystack.optimus.core.v1beta1.GetJobSpecificationsRequest + (*GetJobSpecificationsResponse)(nil), // 41: raystack.optimus.core.v1beta1.GetJobSpecificationsResponse + (*JobSpecificationResponse)(nil), // 42: raystack.optimus.core.v1beta1.JobSpecificationResponse + (*ReplaceAllJobSpecificationsRequest)(nil), // 43: raystack.optimus.core.v1beta1.ReplaceAllJobSpecificationsRequest + (*ReplaceAllJobSpecificationsResponse)(nil), // 44: raystack.optimus.core.v1beta1.ReplaceAllJobSpecificationsResponse + (*GetJobTaskRequest)(nil), // 45: raystack.optimus.core.v1beta1.GetJobTaskRequest + (*GetJobTaskResponse)(nil), // 46: raystack.optimus.core.v1beta1.GetJobTaskResponse + (*JobTask)(nil), // 47: raystack.optimus.core.v1beta1.JobTask + (*GetWindowRequest)(nil), // 48: raystack.optimus.core.v1beta1.GetWindowRequest + (*GetWindowResponse)(nil), // 49: raystack.optimus.core.v1beta1.GetWindowResponse + (*UpdateJobsStateRequest)(nil), // 50: raystack.optimus.core.v1beta1.UpdateJobsStateRequest + (*UpdateJobsStateResponse)(nil), // 51: raystack.optimus.core.v1beta1.UpdateJobsStateResponse + (*SyncJobsStateRequest)(nil), // 52: raystack.optimus.core.v1beta1.SyncJobsStateRequest + (*SyncJobsStateResponse)(nil), // 53: raystack.optimus.core.v1beta1.SyncJobsStateResponse + (*JobInspectResponse_BasicInfoSection)(nil), // 54: raystack.optimus.core.v1beta1.JobInspectResponse.BasicInfoSection + (*JobInspectResponse_JobDependency)(nil), // 55: raystack.optimus.core.v1beta1.JobInspectResponse.JobDependency + (*JobInspectResponse_UpstreamSection)(nil), // 56: raystack.optimus.core.v1beta1.JobInspectResponse.UpstreamSection + (*JobInspectResponse_DownstreamSection)(nil), // 57: raystack.optimus.core.v1beta1.JobInspectResponse.DownstreamSection + (*JobInspectResponse_UpstreamSection_UnknownDependencies)(nil), // 58: raystack.optimus.core.v1beta1.JobInspectResponse.UpstreamSection.UnknownDependencies + nil, // 59: raystack.optimus.core.v1beta1.JobSpecification.AssetsEntry + nil, // 60: raystack.optimus.core.v1beta1.JobSpecification.LabelsEntry + (*JobSpecification_Behavior)(nil), // 61: raystack.optimus.core.v1beta1.JobSpecification.Behavior + (*JobSpecification_Behavior_Retry)(nil), // 62: raystack.optimus.core.v1beta1.JobSpecification.Behavior.Retry + (*JobSpecification_Behavior_Notifiers)(nil), // 63: raystack.optimus.core.v1beta1.JobSpecification.Behavior.Notifiers + nil, // 64: raystack.optimus.core.v1beta1.JobSpecification.Behavior.Notifiers.ConfigEntry + nil, // 65: raystack.optimus.core.v1beta1.HttpDependency.HeadersEntry + nil, // 66: raystack.optimus.core.v1beta1.HttpDependency.ParamsEntry + nil, // 67: raystack.optimus.core.v1beta1.GetDeployJobsStatusResponse.UnknownDependenciesEntry + (*JobTask_Destination)(nil), // 68: raystack.optimus.core.v1beta1.JobTask.Destination + (*JobTask_Dependency)(nil), // 69: raystack.optimus.core.v1beta1.JobTask.Dependency + (*SyncJobsStateRequest_JobStatePair)(nil), // 70: raystack.optimus.core.v1beta1.SyncJobsStateRequest.JobStatePair + (*Log)(nil), // 71: raystack.optimus.core.v1beta1.Log + (*timestamppb.Timestamp)(nil), // 72: google.protobuf.Timestamp + (*structpb.Struct)(nil), // 73: google.protobuf.Struct + (*durationpb.Duration)(nil), // 74: google.protobuf.Duration +} +var file_raystack_optimus_core_v1beta1_job_spec_proto_depIdxs = []int32{ + 25, // 0: raystack.optimus.core.v1beta1.DeployJobSpecificationRequest.jobs:type_name -> raystack.optimus.core.v1beta1.JobSpecification + 71, // 1: raystack.optimus.core.v1beta1.DeployJobSpecificationResponse.log_status:type_name -> raystack.optimus.core.v1beta1.Log + 25, // 2: raystack.optimus.core.v1beta1.AddJobSpecificationsRequest.specs:type_name -> raystack.optimus.core.v1beta1.JobSpecification + 25, // 3: raystack.optimus.core.v1beta1.UpdateJobSpecificationsRequest.specs:type_name -> raystack.optimus.core.v1beta1.JobSpecification + 25, // 4: raystack.optimus.core.v1beta1.JobInspectRequest.spec:type_name -> raystack.optimus.core.v1beta1.JobSpecification + 72, // 5: raystack.optimus.core.v1beta1.JobInspectRequest.scheduled_at:type_name -> google.protobuf.Timestamp + 72, // 6: raystack.optimus.core.v1beta1.JobRun.scheduled_at:type_name -> google.protobuf.Timestamp + 54, // 7: raystack.optimus.core.v1beta1.JobInspectResponse.basic_info:type_name -> raystack.optimus.core.v1beta1.JobInspectResponse.BasicInfoSection + 56, // 8: raystack.optimus.core.v1beta1.JobInspectResponse.upstreams:type_name -> raystack.optimus.core.v1beta1.JobInspectResponse.UpstreamSection + 57, // 9: raystack.optimus.core.v1beta1.JobInspectResponse.downstreams:type_name -> raystack.optimus.core.v1beta1.JobInspectResponse.DownstreamSection + 25, // 10: raystack.optimus.core.v1beta1.CreateJobSpecificationRequest.spec:type_name -> raystack.optimus.core.v1beta1.JobSpecification + 25, // 11: raystack.optimus.core.v1beta1.GetJobSpecificationResponse.spec:type_name -> raystack.optimus.core.v1beta1.JobSpecification + 25, // 12: raystack.optimus.core.v1beta1.ListJobSpecificationResponse.jobs:type_name -> raystack.optimus.core.v1beta1.JobSpecification + 25, // 13: raystack.optimus.core.v1beta1.CheckJobSpecificationRequest.job:type_name -> raystack.optimus.core.v1beta1.JobSpecification + 25, // 14: raystack.optimus.core.v1beta1.CheckJobSpecificationsRequest.jobs:type_name -> raystack.optimus.core.v1beta1.JobSpecification + 71, // 15: raystack.optimus.core.v1beta1.CheckJobSpecificationsResponse.log_status:type_name -> raystack.optimus.core.v1beta1.Log + 29, // 16: raystack.optimus.core.v1beta1.JobSpecification.config:type_name -> raystack.optimus.core.v1beta1.JobConfigItem + 26, // 17: raystack.optimus.core.v1beta1.JobSpecification.dependencies:type_name -> raystack.optimus.core.v1beta1.JobDependency + 59, // 18: raystack.optimus.core.v1beta1.JobSpecification.assets:type_name -> raystack.optimus.core.v1beta1.JobSpecification.AssetsEntry + 28, // 19: raystack.optimus.core.v1beta1.JobSpecification.hooks:type_name -> raystack.optimus.core.v1beta1.JobSpecHook + 60, // 20: raystack.optimus.core.v1beta1.JobSpecification.labels:type_name -> raystack.optimus.core.v1beta1.JobSpecification.LabelsEntry + 61, // 21: raystack.optimus.core.v1beta1.JobSpecification.behavior:type_name -> raystack.optimus.core.v1beta1.JobSpecification.Behavior + 31, // 22: raystack.optimus.core.v1beta1.JobSpecification.metadata:type_name -> raystack.optimus.core.v1beta1.JobMetadata + 27, // 23: raystack.optimus.core.v1beta1.JobDependency.http_dependency:type_name -> raystack.optimus.core.v1beta1.HttpDependency + 65, // 24: raystack.optimus.core.v1beta1.HttpDependency.headers:type_name -> raystack.optimus.core.v1beta1.HttpDependency.HeadersEntry + 66, // 25: raystack.optimus.core.v1beta1.HttpDependency.params:type_name -> raystack.optimus.core.v1beta1.HttpDependency.ParamsEntry + 29, // 26: raystack.optimus.core.v1beta1.JobSpecHook.config:type_name -> raystack.optimus.core.v1beta1.JobConfigItem + 1, // 27: raystack.optimus.core.v1beta1.JobEvent.type:type_name -> raystack.optimus.core.v1beta1.JobEvent.Type + 73, // 28: raystack.optimus.core.v1beta1.JobEvent.value:type_name -> google.protobuf.Struct + 32, // 29: raystack.optimus.core.v1beta1.JobMetadata.resource:type_name -> raystack.optimus.core.v1beta1.JobSpecMetadataResource + 34, // 30: raystack.optimus.core.v1beta1.JobMetadata.airflow:type_name -> raystack.optimus.core.v1beta1.JobSpecMetadataAirflow + 33, // 31: raystack.optimus.core.v1beta1.JobSpecMetadataResource.request:type_name -> raystack.optimus.core.v1beta1.JobSpecMetadataResourceConfig + 33, // 32: raystack.optimus.core.v1beta1.JobSpecMetadataResource.limit:type_name -> raystack.optimus.core.v1beta1.JobSpecMetadataResourceConfig + 71, // 33: raystack.optimus.core.v1beta1.RefreshJobsResponse.log_status:type_name -> raystack.optimus.core.v1beta1.Log + 39, // 34: raystack.optimus.core.v1beta1.GetDeployJobsStatusResponse.failures:type_name -> raystack.optimus.core.v1beta1.DeployJobFailure + 67, // 35: raystack.optimus.core.v1beta1.GetDeployJobsStatusResponse.unknown_dependencies:type_name -> raystack.optimus.core.v1beta1.GetDeployJobsStatusResponse.UnknownDependenciesEntry + 25, // 36: raystack.optimus.core.v1beta1.GetJobSpecificationsResponse.jobs:type_name -> raystack.optimus.core.v1beta1.JobSpecification + 42, // 37: raystack.optimus.core.v1beta1.GetJobSpecificationsResponse.job_specification_responses:type_name -> raystack.optimus.core.v1beta1.JobSpecificationResponse + 25, // 38: raystack.optimus.core.v1beta1.JobSpecificationResponse.job:type_name -> raystack.optimus.core.v1beta1.JobSpecification + 25, // 39: raystack.optimus.core.v1beta1.ReplaceAllJobSpecificationsRequest.jobs:type_name -> raystack.optimus.core.v1beta1.JobSpecification + 71, // 40: raystack.optimus.core.v1beta1.ReplaceAllJobSpecificationsResponse.log_status:type_name -> raystack.optimus.core.v1beta1.Log + 47, // 41: raystack.optimus.core.v1beta1.GetJobTaskResponse.task:type_name -> raystack.optimus.core.v1beta1.JobTask + 68, // 42: raystack.optimus.core.v1beta1.JobTask.destination:type_name -> raystack.optimus.core.v1beta1.JobTask.Destination + 69, // 43: raystack.optimus.core.v1beta1.JobTask.dependencies:type_name -> raystack.optimus.core.v1beta1.JobTask.Dependency + 72, // 44: raystack.optimus.core.v1beta1.GetWindowRequest.scheduled_at:type_name -> google.protobuf.Timestamp + 72, // 45: raystack.optimus.core.v1beta1.GetWindowResponse.start:type_name -> google.protobuf.Timestamp + 72, // 46: raystack.optimus.core.v1beta1.GetWindowResponse.end:type_name -> google.protobuf.Timestamp + 0, // 47: raystack.optimus.core.v1beta1.UpdateJobsStateRequest.state:type_name -> raystack.optimus.core.v1beta1.JobState + 70, // 48: raystack.optimus.core.v1beta1.SyncJobsStateRequest.job_states:type_name -> raystack.optimus.core.v1beta1.SyncJobsStateRequest.JobStatePair + 25, // 49: raystack.optimus.core.v1beta1.JobInspectResponse.BasicInfoSection.job:type_name -> raystack.optimus.core.v1beta1.JobSpecification + 71, // 50: raystack.optimus.core.v1beta1.JobInspectResponse.BasicInfoSection.notice:type_name -> raystack.optimus.core.v1beta1.Log + 9, // 51: raystack.optimus.core.v1beta1.JobInspectResponse.JobDependency.runs:type_name -> raystack.optimus.core.v1beta1.JobRun + 55, // 52: raystack.optimus.core.v1beta1.JobInspectResponse.UpstreamSection.external_dependency:type_name -> raystack.optimus.core.v1beta1.JobInspectResponse.JobDependency + 55, // 53: raystack.optimus.core.v1beta1.JobInspectResponse.UpstreamSection.internal_dependency:type_name -> raystack.optimus.core.v1beta1.JobInspectResponse.JobDependency + 27, // 54: raystack.optimus.core.v1beta1.JobInspectResponse.UpstreamSection.http_dependency:type_name -> raystack.optimus.core.v1beta1.HttpDependency + 58, // 55: raystack.optimus.core.v1beta1.JobInspectResponse.UpstreamSection.unknown_dependencies:type_name -> raystack.optimus.core.v1beta1.JobInspectResponse.UpstreamSection.UnknownDependencies + 71, // 56: raystack.optimus.core.v1beta1.JobInspectResponse.UpstreamSection.notice:type_name -> raystack.optimus.core.v1beta1.Log + 55, // 57: raystack.optimus.core.v1beta1.JobInspectResponse.DownstreamSection.downstream_jobs:type_name -> raystack.optimus.core.v1beta1.JobInspectResponse.JobDependency + 71, // 58: raystack.optimus.core.v1beta1.JobInspectResponse.DownstreamSection.notice:type_name -> raystack.optimus.core.v1beta1.Log + 62, // 59: raystack.optimus.core.v1beta1.JobSpecification.Behavior.retry:type_name -> raystack.optimus.core.v1beta1.JobSpecification.Behavior.Retry + 63, // 60: raystack.optimus.core.v1beta1.JobSpecification.Behavior.notify:type_name -> raystack.optimus.core.v1beta1.JobSpecification.Behavior.Notifiers + 74, // 61: raystack.optimus.core.v1beta1.JobSpecification.Behavior.Retry.delay:type_name -> google.protobuf.Duration + 1, // 62: raystack.optimus.core.v1beta1.JobSpecification.Behavior.Notifiers.on:type_name -> raystack.optimus.core.v1beta1.JobEvent.Type + 64, // 63: raystack.optimus.core.v1beta1.JobSpecification.Behavior.Notifiers.config:type_name -> raystack.optimus.core.v1beta1.JobSpecification.Behavior.Notifiers.ConfigEntry + 0, // 64: raystack.optimus.core.v1beta1.SyncJobsStateRequest.JobStatePair.state:type_name -> raystack.optimus.core.v1beta1.JobState + 2, // 65: raystack.optimus.core.v1beta1.JobSpecificationService.DeployJobSpecification:input_type -> raystack.optimus.core.v1beta1.DeployJobSpecificationRequest + 8, // 66: raystack.optimus.core.v1beta1.JobSpecificationService.JobInspect:input_type -> raystack.optimus.core.v1beta1.JobInspectRequest + 11, // 67: raystack.optimus.core.v1beta1.JobSpecificationService.CreateJobSpecification:input_type -> raystack.optimus.core.v1beta1.CreateJobSpecificationRequest + 4, // 68: raystack.optimus.core.v1beta1.JobSpecificationService.AddJobSpecifications:input_type -> raystack.optimus.core.v1beta1.AddJobSpecificationsRequest + 6, // 69: raystack.optimus.core.v1beta1.JobSpecificationService.UpdateJobSpecifications:input_type -> raystack.optimus.core.v1beta1.UpdateJobSpecificationsRequest + 13, // 70: raystack.optimus.core.v1beta1.JobSpecificationService.GetJobSpecification:input_type -> raystack.optimus.core.v1beta1.GetJobSpecificationRequest + 40, // 71: raystack.optimus.core.v1beta1.JobSpecificationService.GetJobSpecifications:input_type -> raystack.optimus.core.v1beta1.GetJobSpecificationsRequest + 15, // 72: raystack.optimus.core.v1beta1.JobSpecificationService.DeleteJobSpecification:input_type -> raystack.optimus.core.v1beta1.DeleteJobSpecificationRequest + 17, // 73: raystack.optimus.core.v1beta1.JobSpecificationService.ChangeJobNamespace:input_type -> raystack.optimus.core.v1beta1.ChangeJobNamespaceRequest + 19, // 74: raystack.optimus.core.v1beta1.JobSpecificationService.ListJobSpecification:input_type -> raystack.optimus.core.v1beta1.ListJobSpecificationRequest + 21, // 75: raystack.optimus.core.v1beta1.JobSpecificationService.CheckJobSpecification:input_type -> raystack.optimus.core.v1beta1.CheckJobSpecificationRequest + 23, // 76: raystack.optimus.core.v1beta1.JobSpecificationService.CheckJobSpecifications:input_type -> raystack.optimus.core.v1beta1.CheckJobSpecificationsRequest + 35, // 77: raystack.optimus.core.v1beta1.JobSpecificationService.RefreshJobs:input_type -> raystack.optimus.core.v1beta1.RefreshJobsRequest + 37, // 78: raystack.optimus.core.v1beta1.JobSpecificationService.GetDeployJobsStatus:input_type -> raystack.optimus.core.v1beta1.GetDeployJobsStatusRequest + 43, // 79: raystack.optimus.core.v1beta1.JobSpecificationService.ReplaceAllJobSpecifications:input_type -> raystack.optimus.core.v1beta1.ReplaceAllJobSpecificationsRequest + 45, // 80: raystack.optimus.core.v1beta1.JobSpecificationService.GetJobTask:input_type -> raystack.optimus.core.v1beta1.GetJobTaskRequest + 48, // 81: raystack.optimus.core.v1beta1.JobSpecificationService.GetWindow:input_type -> raystack.optimus.core.v1beta1.GetWindowRequest + 50, // 82: raystack.optimus.core.v1beta1.JobSpecificationService.UpdateJobsState:input_type -> raystack.optimus.core.v1beta1.UpdateJobsStateRequest + 52, // 83: raystack.optimus.core.v1beta1.JobSpecificationService.SyncJobsState:input_type -> raystack.optimus.core.v1beta1.SyncJobsStateRequest + 3, // 84: raystack.optimus.core.v1beta1.JobSpecificationService.DeployJobSpecification:output_type -> raystack.optimus.core.v1beta1.DeployJobSpecificationResponse + 10, // 85: raystack.optimus.core.v1beta1.JobSpecificationService.JobInspect:output_type -> raystack.optimus.core.v1beta1.JobInspectResponse + 12, // 86: raystack.optimus.core.v1beta1.JobSpecificationService.CreateJobSpecification:output_type -> raystack.optimus.core.v1beta1.CreateJobSpecificationResponse + 5, // 87: raystack.optimus.core.v1beta1.JobSpecificationService.AddJobSpecifications:output_type -> raystack.optimus.core.v1beta1.AddJobSpecificationsResponse + 7, // 88: raystack.optimus.core.v1beta1.JobSpecificationService.UpdateJobSpecifications:output_type -> raystack.optimus.core.v1beta1.UpdateJobSpecificationsResponse + 14, // 89: raystack.optimus.core.v1beta1.JobSpecificationService.GetJobSpecification:output_type -> raystack.optimus.core.v1beta1.GetJobSpecificationResponse + 41, // 90: raystack.optimus.core.v1beta1.JobSpecificationService.GetJobSpecifications:output_type -> raystack.optimus.core.v1beta1.GetJobSpecificationsResponse + 16, // 91: raystack.optimus.core.v1beta1.JobSpecificationService.DeleteJobSpecification:output_type -> raystack.optimus.core.v1beta1.DeleteJobSpecificationResponse + 18, // 92: raystack.optimus.core.v1beta1.JobSpecificationService.ChangeJobNamespace:output_type -> raystack.optimus.core.v1beta1.ChangeJobNamespaceResponse + 20, // 93: raystack.optimus.core.v1beta1.JobSpecificationService.ListJobSpecification:output_type -> raystack.optimus.core.v1beta1.ListJobSpecificationResponse + 22, // 94: raystack.optimus.core.v1beta1.JobSpecificationService.CheckJobSpecification:output_type -> raystack.optimus.core.v1beta1.CheckJobSpecificationResponse + 24, // 95: raystack.optimus.core.v1beta1.JobSpecificationService.CheckJobSpecifications:output_type -> raystack.optimus.core.v1beta1.CheckJobSpecificationsResponse + 36, // 96: raystack.optimus.core.v1beta1.JobSpecificationService.RefreshJobs:output_type -> raystack.optimus.core.v1beta1.RefreshJobsResponse + 38, // 97: raystack.optimus.core.v1beta1.JobSpecificationService.GetDeployJobsStatus:output_type -> raystack.optimus.core.v1beta1.GetDeployJobsStatusResponse + 44, // 98: raystack.optimus.core.v1beta1.JobSpecificationService.ReplaceAllJobSpecifications:output_type -> raystack.optimus.core.v1beta1.ReplaceAllJobSpecificationsResponse + 46, // 99: raystack.optimus.core.v1beta1.JobSpecificationService.GetJobTask:output_type -> raystack.optimus.core.v1beta1.GetJobTaskResponse + 49, // 100: raystack.optimus.core.v1beta1.JobSpecificationService.GetWindow:output_type -> raystack.optimus.core.v1beta1.GetWindowResponse + 51, // 101: raystack.optimus.core.v1beta1.JobSpecificationService.UpdateJobsState:output_type -> raystack.optimus.core.v1beta1.UpdateJobsStateResponse + 53, // 102: raystack.optimus.core.v1beta1.JobSpecificationService.SyncJobsState:output_type -> raystack.optimus.core.v1beta1.SyncJobsStateResponse + 84, // [84:103] is the sub-list for method output_type + 65, // [65:84] is the sub-list for method input_type + 65, // [65:65] is the sub-list for extension type_name + 65, // [65:65] is the sub-list for extension extendee + 0, // [0:65] is the sub-list for field type_name +} + +func init() { file_raystack_optimus_core_v1beta1_job_spec_proto_init() } +func file_raystack_optimus_core_v1beta1_job_spec_proto_init() { + if File_raystack_optimus_core_v1beta1_job_spec_proto != nil { return } - file_odpf_optimus_core_v1beta1_status_proto_init() + file_raystack_optimus_core_v1beta1_status_proto_init() if !protoimpl.UnsafeEnabled { - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DeployJobSpecificationRequest); i { case 0: return &v.state @@ -4667,7 +5253,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DeployJobSpecificationResponse); i { case 0: return &v.state @@ -4679,7 +5265,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AddJobSpecificationsRequest); i { case 0: return &v.state @@ -4691,7 +5277,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AddJobSpecificationsResponse); i { case 0: return &v.state @@ -4703,7 +5289,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UpdateJobSpecificationsRequest); i { case 0: return &v.state @@ -4715,7 +5301,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UpdateJobSpecificationsResponse); i { case 0: return &v.state @@ -4727,7 +5313,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JobInspectRequest); i { case 0: return &v.state @@ -4739,7 +5325,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JobRun); i { case 0: return &v.state @@ -4751,7 +5337,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JobInspectResponse); i { case 0: return &v.state @@ -4763,7 +5349,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CreateJobSpecificationRequest); i { case 0: return &v.state @@ -4775,7 +5361,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CreateJobSpecificationResponse); i { case 0: return &v.state @@ -4787,7 +5373,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetJobSpecificationRequest); i { case 0: return &v.state @@ -4799,7 +5385,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetJobSpecificationResponse); i { case 0: return &v.state @@ -4811,7 +5397,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DeleteJobSpecificationRequest); i { case 0: return &v.state @@ -4823,7 +5409,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DeleteJobSpecificationResponse); i { case 0: return &v.state @@ -4835,7 +5421,31 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ChangeJobNamespaceRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ChangeJobNamespaceResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListJobSpecificationRequest); i { case 0: return &v.state @@ -4847,7 +5457,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListJobSpecificationResponse); i { case 0: return &v.state @@ -4859,7 +5469,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CheckJobSpecificationRequest); i { case 0: return &v.state @@ -4871,7 +5481,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CheckJobSpecificationResponse); i { case 0: return &v.state @@ -4883,7 +5493,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CheckJobSpecificationsRequest); i { case 0: return &v.state @@ -4895,7 +5505,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CheckJobSpecificationsResponse); i { case 0: return &v.state @@ -4907,7 +5517,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JobSpecification); i { case 0: return &v.state @@ -4919,7 +5529,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JobDependency); i { case 0: return &v.state @@ -4931,7 +5541,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HttpDependency); i { case 0: return &v.state @@ -4943,7 +5553,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JobSpecHook); i { case 0: return &v.state @@ -4955,7 +5565,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JobConfigItem); i { case 0: return &v.state @@ -4967,7 +5577,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JobEvent); i { case 0: return &v.state @@ -4979,7 +5589,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JobMetadata); i { case 0: return &v.state @@ -4991,7 +5601,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JobSpecMetadataResource); i { case 0: return &v.state @@ -5003,7 +5613,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JobSpecMetadataResourceConfig); i { case 0: return &v.state @@ -5015,7 +5625,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JobSpecMetadataAirflow); i { case 0: return &v.state @@ -5027,7 +5637,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RefreshJobsRequest); i { case 0: return &v.state @@ -5039,7 +5649,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RefreshJobsResponse); i { case 0: return &v.state @@ -5051,7 +5661,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetDeployJobsStatusRequest); i { case 0: return &v.state @@ -5063,7 +5673,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetDeployJobsStatusResponse); i { case 0: return &v.state @@ -5075,7 +5685,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DeployJobFailure); i { case 0: return &v.state @@ -5087,7 +5697,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetJobSpecificationsRequest); i { case 0: return &v.state @@ -5099,7 +5709,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetJobSpecificationsResponse); i { case 0: return &v.state @@ -5111,7 +5721,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JobSpecificationResponse); i { case 0: return &v.state @@ -5123,7 +5733,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ReplaceAllJobSpecificationsRequest); i { case 0: return &v.state @@ -5135,7 +5745,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ReplaceAllJobSpecificationsResponse); i { case 0: return &v.state @@ -5147,7 +5757,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetJobTaskRequest); i { case 0: return &v.state @@ -5159,7 +5769,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetJobTaskResponse); i { case 0: return &v.state @@ -5171,7 +5781,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JobTask); i { case 0: return &v.state @@ -5183,7 +5793,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetWindowRequest); i { case 0: return &v.state @@ -5195,7 +5805,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetWindowResponse); i { case 0: return &v.state @@ -5207,7 +5817,55 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateJobsStateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateJobsStateResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SyncJobsStateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SyncJobsStateResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JobInspectResponse_BasicInfoSection); i { case 0: return &v.state @@ -5219,7 +5877,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JobInspectResponse_JobDependency); i { case 0: return &v.state @@ -5231,7 +5889,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JobInspectResponse_UpstreamSection); i { case 0: return &v.state @@ -5243,7 +5901,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JobInspectResponse_DownstreamSection); i { case 0: return &v.state @@ -5255,7 +5913,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JobInspectResponse_UpstreamSection_UnknownDependencies); i { case 0: return &v.state @@ -5267,7 +5925,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JobSpecification_Behavior); i { case 0: return &v.state @@ -5279,7 +5937,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JobSpecification_Behavior_Retry); i { case 0: return &v.state @@ -5291,7 +5949,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JobSpecification_Behavior_Notifiers); i { case 0: return &v.state @@ -5303,7 +5961,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JobTask_Destination); i { case 0: return &v.state @@ -5315,7 +5973,7 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } - file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JobTask_Dependency); i { case 0: return &v.state @@ -5327,24 +5985,36 @@ func file_odpf_optimus_core_v1beta1_job_spec_proto_init() { return nil } } + file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes[68].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SyncJobsStateRequest_JobStatePair); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_odpf_optimus_core_v1beta1_job_spec_proto_rawDesc, - NumEnums: 1, - NumMessages: 62, + RawDescriptor: file_raystack_optimus_core_v1beta1_job_spec_proto_rawDesc, + NumEnums: 2, + NumMessages: 69, NumExtensions: 0, NumServices: 1, }, - GoTypes: file_odpf_optimus_core_v1beta1_job_spec_proto_goTypes, - DependencyIndexes: file_odpf_optimus_core_v1beta1_job_spec_proto_depIdxs, - EnumInfos: file_odpf_optimus_core_v1beta1_job_spec_proto_enumTypes, - MessageInfos: file_odpf_optimus_core_v1beta1_job_spec_proto_msgTypes, + GoTypes: file_raystack_optimus_core_v1beta1_job_spec_proto_goTypes, + DependencyIndexes: file_raystack_optimus_core_v1beta1_job_spec_proto_depIdxs, + EnumInfos: file_raystack_optimus_core_v1beta1_job_spec_proto_enumTypes, + MessageInfos: file_raystack_optimus_core_v1beta1_job_spec_proto_msgTypes, }.Build() - File_odpf_optimus_core_v1beta1_job_spec_proto = out.File - file_odpf_optimus_core_v1beta1_job_spec_proto_rawDesc = nil - file_odpf_optimus_core_v1beta1_job_spec_proto_goTypes = nil - file_odpf_optimus_core_v1beta1_job_spec_proto_depIdxs = nil + File_raystack_optimus_core_v1beta1_job_spec_proto = out.File + file_raystack_optimus_core_v1beta1_job_spec_proto_rawDesc = nil + file_raystack_optimus_core_v1beta1_job_spec_proto_goTypes = nil + file_raystack_optimus_core_v1beta1_job_spec_proto_depIdxs = nil } diff --git a/protos/odpf/optimus/core/v1beta1/job_spec.pb.gw.go b/protos/raystack/optimus/core/v1beta1/job_spec.pb.gw.go similarity index 74% rename from protos/odpf/optimus/core/v1beta1/job_spec.pb.gw.go rename to protos/raystack/optimus/core/v1beta1/job_spec.pb.gw.go index 1215cd1787..5c05a89515 100644 --- a/protos/odpf/optimus/core/v1beta1/job_spec.pb.gw.go +++ b/protos/raystack/optimus/core/v1beta1/job_spec.pb.gw.go @@ -1,5 +1,5 @@ // Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. -// source: odpf/optimus/core/v1beta1/job_spec.proto +// source: raystack/optimus/core/v1beta1/job_spec.proto /* Package optimus is a reverse proxy. @@ -621,6 +621,74 @@ func local_request_JobSpecificationService_DeleteJobSpecification_0(ctx context. } +func request_JobSpecificationService_ChangeJobNamespace_0(ctx context.Context, marshaler runtime.Marshaler, client JobSpecificationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ChangeJobNamespaceRequest + 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); 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["project_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "project_name") + } + + protoReq.ProjectName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "project_name", err) + } + + msg, err := client.ChangeJobNamespace(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_JobSpecificationService_ChangeJobNamespace_0(ctx context.Context, marshaler runtime.Marshaler, server JobSpecificationServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ChangeJobNamespaceRequest + 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); 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["project_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "project_name") + } + + protoReq.ProjectName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "project_name", err) + } + + msg, err := server.ChangeJobNamespace(ctx, &protoReq) + return msg, metadata, err + +} + func request_JobSpecificationService_ListJobSpecification_0(ctx context.Context, marshaler runtime.Marshaler, client JobSpecificationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq ListJobSpecificationRequest var metadata runtime.ServerMetadata @@ -891,6 +959,182 @@ func local_request_JobSpecificationService_GetWindow_0(ctx context.Context, mars } +func request_JobSpecificationService_UpdateJobsState_0(ctx context.Context, marshaler runtime.Marshaler, client JobSpecificationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq UpdateJobsStateRequest + 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); 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["project_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "project_name") + } + + protoReq.ProjectName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "project_name", err) + } + + val, ok = pathParams["namespace_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "namespace_name") + } + + protoReq.NamespaceName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "namespace_name", err) + } + + msg, err := client.UpdateJobsState(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_JobSpecificationService_UpdateJobsState_0(ctx context.Context, marshaler runtime.Marshaler, server JobSpecificationServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq UpdateJobsStateRequest + 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); 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["project_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "project_name") + } + + protoReq.ProjectName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "project_name", err) + } + + val, ok = pathParams["namespace_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "namespace_name") + } + + protoReq.NamespaceName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "namespace_name", err) + } + + msg, err := server.UpdateJobsState(ctx, &protoReq) + return msg, metadata, err + +} + +func request_JobSpecificationService_SyncJobsState_0(ctx context.Context, marshaler runtime.Marshaler, client JobSpecificationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq SyncJobsStateRequest + 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); 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["project_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "project_name") + } + + protoReq.ProjectName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "project_name", err) + } + + val, ok = pathParams["namespace_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "namespace_name") + } + + protoReq.NamespaceName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "namespace_name", err) + } + + msg, err := client.SyncJobsState(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_JobSpecificationService_SyncJobsState_0(ctx context.Context, marshaler runtime.Marshaler, server JobSpecificationServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq SyncJobsStateRequest + 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); 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["project_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "project_name") + } + + protoReq.ProjectName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "project_name", err) + } + + val, ok = pathParams["namespace_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "namespace_name") + } + + protoReq.NamespaceName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "namespace_name", err) + } + + msg, err := server.SyncJobsState(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterJobSpecificationServiceHandlerServer registers the http handlers for service JobSpecificationService to "mux". // UnaryRPC :call JobSpecificationServiceServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -903,7 +1147,7 @@ func RegisterJobSpecificationServiceHandlerServer(ctx context.Context, mux *runt var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobSpecificationService/JobInspect", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job/inspect")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobSpecificationService/JobInspect", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job/inspect")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -926,7 +1170,7 @@ func RegisterJobSpecificationServiceHandlerServer(ctx context.Context, mux *runt var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobSpecificationService/CreateJobSpecification", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobSpecificationService/CreateJobSpecification", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -949,7 +1193,7 @@ func RegisterJobSpecificationServiceHandlerServer(ctx context.Context, mux *runt var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobSpecificationService/AddJobSpecifications", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/jobs")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobSpecificationService/AddJobSpecifications", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/jobs")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -972,7 +1216,7 @@ func RegisterJobSpecificationServiceHandlerServer(ctx context.Context, mux *runt var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobSpecificationService/UpdateJobSpecifications", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/jobs")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobSpecificationService/UpdateJobSpecifications", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/jobs")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -995,7 +1239,7 @@ func RegisterJobSpecificationServiceHandlerServer(ctx context.Context, mux *runt var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobSpecificationService/GetJobSpecification", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job/{job_name}")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobSpecificationService/GetJobSpecification", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job/{job_name}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -1018,7 +1262,7 @@ func RegisterJobSpecificationServiceHandlerServer(ctx context.Context, mux *runt var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobSpecificationService/GetJobSpecifications", runtime.WithHTTPPathPattern("/v1beta1/jobs")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobSpecificationService/GetJobSpecifications", runtime.WithHTTPPathPattern("/v1beta1/jobs")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -1041,7 +1285,7 @@ func RegisterJobSpecificationServiceHandlerServer(ctx context.Context, mux *runt var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobSpecificationService/DeleteJobSpecification", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job/{job_name}")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobSpecificationService/DeleteJobSpecification", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job/{job_name}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -1058,13 +1302,36 @@ func RegisterJobSpecificationServiceHandlerServer(ctx context.Context, mux *runt }) + mux.Handle("POST", pattern_JobSpecificationService_ChangeJobNamespace_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobSpecificationService/ChangeJobNamespace", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/change-job-namespace")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_JobSpecificationService_ChangeJobNamespace_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_JobSpecificationService_ChangeJobNamespace_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_JobSpecificationService_ListJobSpecification_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobSpecificationService/ListJobSpecification", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobSpecificationService/ListJobSpecification", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -1087,7 +1354,7 @@ func RegisterJobSpecificationServiceHandlerServer(ctx context.Context, mux *runt var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobSpecificationService/CheckJobSpecification", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/job/check")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobSpecificationService/CheckJobSpecification", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/job/check")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -1110,7 +1377,7 @@ func RegisterJobSpecificationServiceHandlerServer(ctx context.Context, mux *runt var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobSpecificationService/GetJobTask", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job/{job_name}/task")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobSpecificationService/GetJobTask", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job/{job_name}/task")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -1133,7 +1400,7 @@ func RegisterJobSpecificationServiceHandlerServer(ctx context.Context, mux *runt var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobSpecificationService/GetWindow", runtime.WithHTTPPathPattern("/v1beta1/window")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobSpecificationService/GetWindow", runtime.WithHTTPPathPattern("/v1beta1/window")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -1150,6 +1417,52 @@ func RegisterJobSpecificationServiceHandlerServer(ctx context.Context, mux *runt }) + mux.Handle("PATCH", pattern_JobSpecificationService_UpdateJobsState_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobSpecificationService/UpdateJobsState", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/update-job-state")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_JobSpecificationService_UpdateJobsState_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_JobSpecificationService_UpdateJobsState_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("PATCH", pattern_JobSpecificationService_SyncJobsState_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobSpecificationService/SyncJobsState", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/sync-job-state")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_JobSpecificationService_SyncJobsState_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_JobSpecificationService_SyncJobsState_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -1195,7 +1508,7 @@ func RegisterJobSpecificationServiceHandlerClient(ctx context.Context, mux *runt ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobSpecificationService/JobInspect", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job/inspect")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobSpecificationService/JobInspect", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job/inspect")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -1215,7 +1528,7 @@ func RegisterJobSpecificationServiceHandlerClient(ctx context.Context, mux *runt ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobSpecificationService/CreateJobSpecification", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobSpecificationService/CreateJobSpecification", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -1235,7 +1548,7 @@ func RegisterJobSpecificationServiceHandlerClient(ctx context.Context, mux *runt ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobSpecificationService/AddJobSpecifications", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/jobs")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobSpecificationService/AddJobSpecifications", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/jobs")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -1255,7 +1568,7 @@ func RegisterJobSpecificationServiceHandlerClient(ctx context.Context, mux *runt ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobSpecificationService/UpdateJobSpecifications", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/jobs")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobSpecificationService/UpdateJobSpecifications", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/jobs")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -1275,7 +1588,7 @@ func RegisterJobSpecificationServiceHandlerClient(ctx context.Context, mux *runt ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobSpecificationService/GetJobSpecification", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job/{job_name}")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobSpecificationService/GetJobSpecification", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job/{job_name}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -1295,7 +1608,7 @@ func RegisterJobSpecificationServiceHandlerClient(ctx context.Context, mux *runt ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobSpecificationService/GetJobSpecifications", runtime.WithHTTPPathPattern("/v1beta1/jobs")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobSpecificationService/GetJobSpecifications", runtime.WithHTTPPathPattern("/v1beta1/jobs")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -1315,7 +1628,7 @@ func RegisterJobSpecificationServiceHandlerClient(ctx context.Context, mux *runt ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobSpecificationService/DeleteJobSpecification", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job/{job_name}")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobSpecificationService/DeleteJobSpecification", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job/{job_name}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -1331,11 +1644,31 @@ func RegisterJobSpecificationServiceHandlerClient(ctx context.Context, mux *runt }) + mux.Handle("POST", pattern_JobSpecificationService_ChangeJobNamespace_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, "/raystack.optimus.core.v1beta1.JobSpecificationService/ChangeJobNamespace", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/change-job-namespace")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_JobSpecificationService_ChangeJobNamespace_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_JobSpecificationService_ChangeJobNamespace_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_JobSpecificationService_ListJobSpecification_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, "/odpf.optimus.core.v1beta1.JobSpecificationService/ListJobSpecification", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobSpecificationService/ListJobSpecification", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -1355,7 +1688,7 @@ func RegisterJobSpecificationServiceHandlerClient(ctx context.Context, mux *runt ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobSpecificationService/CheckJobSpecification", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/job/check")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobSpecificationService/CheckJobSpecification", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/job/check")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -1375,7 +1708,7 @@ func RegisterJobSpecificationServiceHandlerClient(ctx context.Context, mux *runt ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobSpecificationService/GetJobTask", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job/{job_name}/task")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobSpecificationService/GetJobTask", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/job/{job_name}/task")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -1395,7 +1728,7 @@ func RegisterJobSpecificationServiceHandlerClient(ctx context.Context, mux *runt ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.JobSpecificationService/GetWindow", runtime.WithHTTPPathPattern("/v1beta1/window")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.JobSpecificationService/GetWindow", runtime.WithHTTPPathPattern("/v1beta1/window")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -1411,6 +1744,46 @@ func RegisterJobSpecificationServiceHandlerClient(ctx context.Context, mux *runt }) + mux.Handle("PATCH", pattern_JobSpecificationService_UpdateJobsState_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, "/raystack.optimus.core.v1beta1.JobSpecificationService/UpdateJobsState", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/update-job-state")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_JobSpecificationService_UpdateJobsState_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_JobSpecificationService_UpdateJobsState_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("PATCH", pattern_JobSpecificationService_SyncJobsState_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, "/raystack.optimus.core.v1beta1.JobSpecificationService/SyncJobsState", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/sync-job-state")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_JobSpecificationService_SyncJobsState_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_JobSpecificationService_SyncJobsState_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -1429,6 +1802,8 @@ var ( pattern_JobSpecificationService_DeleteJobSpecification_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{"v1beta1", "project", "project_name", "namespace", "namespace_name", "job", "job_name"}, "")) + pattern_JobSpecificationService_ChangeJobNamespace_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"v1beta1", "project", "project_name", "change-job-namespace"}, "")) + pattern_JobSpecificationService_ListJobSpecification_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{"v1beta1", "project", "project_name", "namespace", "namespace_name", "job"}, "")) pattern_JobSpecificationService_CheckJobSpecification_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 2, 4}, []string{"v1beta1", "project", "project_name", "job", "check"}, "")) @@ -1436,6 +1811,10 @@ var ( pattern_JobSpecificationService_GetJobTask_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, 2, 7}, []string{"v1beta1", "project", "project_name", "namespace", "namespace_name", "job", "job_name", "task"}, "")) pattern_JobSpecificationService_GetWindow_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1beta1", "window"}, "")) + + pattern_JobSpecificationService_UpdateJobsState_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{"v1beta1", "project", "project_name", "namespace", "namespace_name", "update-job-state"}, "")) + + pattern_JobSpecificationService_SyncJobsState_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{"v1beta1", "project", "project_name", "namespace", "namespace_name", "sync-job-state"}, "")) ) var ( @@ -1453,6 +1832,8 @@ var ( forward_JobSpecificationService_DeleteJobSpecification_0 = runtime.ForwardResponseMessage + forward_JobSpecificationService_ChangeJobNamespace_0 = runtime.ForwardResponseMessage + forward_JobSpecificationService_ListJobSpecification_0 = runtime.ForwardResponseMessage forward_JobSpecificationService_CheckJobSpecification_0 = runtime.ForwardResponseMessage @@ -1460,4 +1841,8 @@ var ( forward_JobSpecificationService_GetJobTask_0 = runtime.ForwardResponseMessage forward_JobSpecificationService_GetWindow_0 = runtime.ForwardResponseMessage + + forward_JobSpecificationService_UpdateJobsState_0 = runtime.ForwardResponseMessage + + forward_JobSpecificationService_SyncJobsState_0 = runtime.ForwardResponseMessage ) diff --git a/protos/odpf/optimus/core/v1beta1/job_spec.swagger.json b/protos/raystack/optimus/core/v1beta1/job_spec.swagger.json similarity index 83% rename from protos/odpf/optimus/core/v1beta1/job_spec.swagger.json rename to protos/raystack/optimus/core/v1beta1/job_spec.swagger.json index 24f606e5e2..75e5912731 100644 --- a/protos/odpf/optimus/core/v1beta1/job_spec.swagger.json +++ b/protos/raystack/optimus/core/v1beta1/job_spec.swagger.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "title": "odpf/optimus/core/v1beta1/job_spec.proto", + "title": "raystack/optimus/core/v1beta1/job_spec.proto", "version": "0.1" }, "tags": [ @@ -11,15 +11,9 @@ ], "host": "127.0.0.1:9100", "basePath": "/api", - "schemes": [ - "http" - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], + "schemes": ["http"], + "consumes": ["application/json"], + "produces": ["application/json"], "paths": { "/v1beta1/jobs": { "get": { @@ -65,9 +59,55 @@ "type": "string" } ], - "tags": [ - "JobSpecificationService" - ] + "tags": ["JobSpecificationService"] + } + }, + "/v1beta1/project/{projectName}/change-job-namespace": { + "post": { + "summary": "ChangeJobNamespace move a job spec from one namespace to another", + "operationId": "JobSpecificationService_ChangeJobNamespace", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1beta1ChangeJobNamespaceResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "projectName", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "namespaceName": { + "type": "string" + }, + "jobName": { + "type": "string" + }, + "newNamespaceName": { + "type": "string" + } + } + } + } + ], + "tags": ["JobSpecificationService"] } }, "/v1beta1/project/{projectName}/job/check": { @@ -96,9 +136,7 @@ "type": "string" } ], - "tags": [ - "JobSpecificationService" - ] + "tags": ["JobSpecificationService"] } }, "/v1beta1/project/{projectName}/namespace/{namespaceName}/job": { @@ -133,9 +171,7 @@ "type": "string" } ], - "tags": [ - "JobSpecificationService" - ] + "tags": ["JobSpecificationService"] }, "post": { "summary": "CreateJobSpecification registers a new job for a namespace which belongs to a project", @@ -181,9 +217,7 @@ } } ], - "tags": [ - "JobSpecificationService" - ] + "tags": ["JobSpecificationService"] } }, "/v1beta1/project/{projectName}/namespace/{namespaceName}/job/inspect": { @@ -238,9 +272,7 @@ } } ], - "tags": [ - "JobSpecificationService" - ] + "tags": ["JobSpecificationService"] } }, "/v1beta1/project/{projectName}/namespace/{namespaceName}/job/{jobName}": { @@ -281,9 +313,7 @@ "type": "string" } ], - "tags": [ - "JobSpecificationService" - ] + "tags": ["JobSpecificationService"] }, "delete": { "summary": "DeleteJobSpecification deletes a job spec of a namespace", @@ -334,9 +364,7 @@ "type": "boolean" } ], - "tags": [ - "JobSpecificationService" - ] + "tags": ["JobSpecificationService"] } }, "/v1beta1/project/{projectName}/namespace/{namespaceName}/job/{jobName}/task": { @@ -377,9 +405,7 @@ "type": "string" } ], - "tags": [ - "JobSpecificationService" - ] + "tags": ["JobSpecificationService"] } }, "/v1beta1/project/{projectName}/namespace/{namespaceName}/jobs": { @@ -430,9 +456,7 @@ } } ], - "tags": [ - "JobSpecificationService" - ] + "tags": ["JobSpecificationService"] }, "put": { "summary": "UpdateJobSpecifications modify jobs for a namespace which belongs to the given project", @@ -481,9 +505,115 @@ } } ], - "tags": [ - "JobSpecificationService" - ] + "tags": ["JobSpecificationService"] + } + }, + "/v1beta1/project/{projectName}/namespace/{namespaceName}/sync-job-state": { + "patch": { + "summary": "SyncJobsState enable / disable job on scheuler", + "operationId": "JobSpecificationService_SyncJobsState", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1beta1SyncJobsStateResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "projectName", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "namespaceName", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "jobStates": { + "type": "array", + "items": { + "$ref": "#/definitions/SyncJobsStateRequestJobStatePair" + } + } + } + } + } + ], + "tags": ["JobSpecificationService"] + } + }, + "/v1beta1/project/{projectName}/namespace/{namespaceName}/update-job-state": { + "patch": { + "summary": "UpdateJobState enable / disable job on scheuler", + "operationId": "JobSpecificationService_UpdateJobsState", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1beta1UpdateJobsStateResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "projectName", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "namespaceName", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "remark": { + "type": "string" + }, + "state": { + "$ref": "#/definitions/v1beta1JobState" + }, + "jobNames": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + ], + "tags": ["JobSpecificationService"] } }, "/v1beta1/window": { @@ -538,9 +668,7 @@ "format": "int32" } ], - "tags": [ - "JobSpecificationService" - ] + "tags": ["JobSpecificationService"] } } }, @@ -690,6 +818,17 @@ } } }, + "SyncJobsStateRequestJobStatePair": { + "type": "object", + "properties": { + "jobName": { + "type": "string" + }, + "state": { + "$ref": "#/definitions/v1beta1JobState" + } + } + }, "UpstreamSectionUnknownDependencies": { "type": "object", "properties": { @@ -756,6 +895,9 @@ } } }, + "v1beta1ChangeJobNamespaceResponse": { + "type": "object" + }, "v1beta1CheckJobSpecificationResponse": { "type": "object", "properties": { @@ -1161,6 +1303,15 @@ } } }, + "v1beta1JobState": { + "type": "string", + "enum": [ + "JOB_STATE_UNSPECIFIED", + "JOB_STATE_ENABLED", + "JOB_STATE_DISABLED" + ], + "default": "JOB_STATE_UNSPECIFIED" + }, "v1beta1JobTask": { "type": "object", "properties": { @@ -1236,6 +1387,9 @@ } } }, + "v1beta1SyncJobsStateResponse": { + "type": "object" + }, "v1beta1UpdateJobSpecificationsResponse": { "type": "object", "properties": { @@ -1243,6 +1397,9 @@ "type": "string" } } + }, + "v1beta1UpdateJobsStateResponse": { + "type": "object" } }, "externalDocs": { diff --git a/protos/odpf/optimus/core/v1beta1/job_spec_grpc.pb.go b/protos/raystack/optimus/core/v1beta1/job_spec_grpc.pb.go similarity index 79% rename from protos/odpf/optimus/core/v1beta1/job_spec_grpc.pb.go rename to protos/raystack/optimus/core/v1beta1/job_spec_grpc.pb.go index deb6fec560..0b9d121034 100644 --- a/protos/odpf/optimus/core/v1beta1/job_spec_grpc.pb.go +++ b/protos/raystack/optimus/core/v1beta1/job_spec_grpc.pb.go @@ -2,7 +2,7 @@ // versions: // - protoc-gen-go-grpc v1.2.0 // - protoc (unknown) -// source: odpf/optimus/core/v1beta1/job_spec.proto +// source: raystack/optimus/core/v1beta1/job_spec.proto package optimus @@ -42,6 +42,8 @@ type JobSpecificationServiceClient interface { GetJobSpecifications(ctx context.Context, in *GetJobSpecificationsRequest, opts ...grpc.CallOption) (*GetJobSpecificationsResponse, error) // DeleteJobSpecification deletes a job spec of a namespace DeleteJobSpecification(ctx context.Context, in *DeleteJobSpecificationRequest, opts ...grpc.CallOption) (*DeleteJobSpecificationResponse, error) + // ChangeJobNamespace move a job spec from one namespace to another + ChangeJobNamespace(ctx context.Context, in *ChangeJobNamespaceRequest, opts ...grpc.CallOption) (*ChangeJobNamespaceResponse, error) // ListJobSpecification returns list of jobs created in a project ListJobSpecification(ctx context.Context, in *ListJobSpecificationRequest, opts ...grpc.CallOption) (*ListJobSpecificationResponse, error) // CheckJobSpecification checks if a job specification is valid @@ -61,6 +63,10 @@ type JobSpecificationServiceClient interface { // GetWindow provides the start and end dates provided a scheduled date // of the execution window GetWindow(ctx context.Context, in *GetWindowRequest, opts ...grpc.CallOption) (*GetWindowResponse, error) + // UpdateJobState enable / disable job on scheuler + UpdateJobsState(ctx context.Context, in *UpdateJobsStateRequest, opts ...grpc.CallOption) (*UpdateJobsStateResponse, error) + // SyncJobsState enable / disable job on scheuler + SyncJobsState(ctx context.Context, in *SyncJobsStateRequest, opts ...grpc.CallOption) (*SyncJobsStateResponse, error) } type jobSpecificationServiceClient struct { @@ -72,7 +78,7 @@ func NewJobSpecificationServiceClient(cc grpc.ClientConnInterface) JobSpecificat } func (c *jobSpecificationServiceClient) DeployJobSpecification(ctx context.Context, opts ...grpc.CallOption) (JobSpecificationService_DeployJobSpecificationClient, error) { - stream, err := c.cc.NewStream(ctx, &JobSpecificationService_ServiceDesc.Streams[0], "/odpf.optimus.core.v1beta1.JobSpecificationService/DeployJobSpecification", opts...) + stream, err := c.cc.NewStream(ctx, &JobSpecificationService_ServiceDesc.Streams[0], "/raystack.optimus.core.v1beta1.JobSpecificationService/DeployJobSpecification", opts...) if err != nil { return nil, err } @@ -104,7 +110,7 @@ func (x *jobSpecificationServiceDeployJobSpecificationClient) Recv() (*DeployJob func (c *jobSpecificationServiceClient) JobInspect(ctx context.Context, in *JobInspectRequest, opts ...grpc.CallOption) (*JobInspectResponse, error) { out := new(JobInspectResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.JobSpecificationService/JobInspect", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.JobSpecificationService/JobInspect", in, out, opts...) if err != nil { return nil, err } @@ -113,7 +119,7 @@ func (c *jobSpecificationServiceClient) JobInspect(ctx context.Context, in *JobI func (c *jobSpecificationServiceClient) CreateJobSpecification(ctx context.Context, in *CreateJobSpecificationRequest, opts ...grpc.CallOption) (*CreateJobSpecificationResponse, error) { out := new(CreateJobSpecificationResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.JobSpecificationService/CreateJobSpecification", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.JobSpecificationService/CreateJobSpecification", in, out, opts...) if err != nil { return nil, err } @@ -122,7 +128,7 @@ func (c *jobSpecificationServiceClient) CreateJobSpecification(ctx context.Conte func (c *jobSpecificationServiceClient) AddJobSpecifications(ctx context.Context, in *AddJobSpecificationsRequest, opts ...grpc.CallOption) (*AddJobSpecificationsResponse, error) { out := new(AddJobSpecificationsResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.JobSpecificationService/AddJobSpecifications", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.JobSpecificationService/AddJobSpecifications", in, out, opts...) if err != nil { return nil, err } @@ -131,7 +137,7 @@ func (c *jobSpecificationServiceClient) AddJobSpecifications(ctx context.Context func (c *jobSpecificationServiceClient) UpdateJobSpecifications(ctx context.Context, in *UpdateJobSpecificationsRequest, opts ...grpc.CallOption) (*UpdateJobSpecificationsResponse, error) { out := new(UpdateJobSpecificationsResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.JobSpecificationService/UpdateJobSpecifications", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.JobSpecificationService/UpdateJobSpecifications", in, out, opts...) if err != nil { return nil, err } @@ -140,7 +146,7 @@ func (c *jobSpecificationServiceClient) UpdateJobSpecifications(ctx context.Cont func (c *jobSpecificationServiceClient) GetJobSpecification(ctx context.Context, in *GetJobSpecificationRequest, opts ...grpc.CallOption) (*GetJobSpecificationResponse, error) { out := new(GetJobSpecificationResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.JobSpecificationService/GetJobSpecification", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.JobSpecificationService/GetJobSpecification", in, out, opts...) if err != nil { return nil, err } @@ -149,7 +155,7 @@ func (c *jobSpecificationServiceClient) GetJobSpecification(ctx context.Context, func (c *jobSpecificationServiceClient) GetJobSpecifications(ctx context.Context, in *GetJobSpecificationsRequest, opts ...grpc.CallOption) (*GetJobSpecificationsResponse, error) { out := new(GetJobSpecificationsResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.JobSpecificationService/GetJobSpecifications", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.JobSpecificationService/GetJobSpecifications", in, out, opts...) if err != nil { return nil, err } @@ -158,7 +164,16 @@ func (c *jobSpecificationServiceClient) GetJobSpecifications(ctx context.Context func (c *jobSpecificationServiceClient) DeleteJobSpecification(ctx context.Context, in *DeleteJobSpecificationRequest, opts ...grpc.CallOption) (*DeleteJobSpecificationResponse, error) { out := new(DeleteJobSpecificationResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.JobSpecificationService/DeleteJobSpecification", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.JobSpecificationService/DeleteJobSpecification", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *jobSpecificationServiceClient) ChangeJobNamespace(ctx context.Context, in *ChangeJobNamespaceRequest, opts ...grpc.CallOption) (*ChangeJobNamespaceResponse, error) { + out := new(ChangeJobNamespaceResponse) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.JobSpecificationService/ChangeJobNamespace", in, out, opts...) if err != nil { return nil, err } @@ -167,7 +182,7 @@ func (c *jobSpecificationServiceClient) DeleteJobSpecification(ctx context.Conte func (c *jobSpecificationServiceClient) ListJobSpecification(ctx context.Context, in *ListJobSpecificationRequest, opts ...grpc.CallOption) (*ListJobSpecificationResponse, error) { out := new(ListJobSpecificationResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.JobSpecificationService/ListJobSpecification", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.JobSpecificationService/ListJobSpecification", in, out, opts...) if err != nil { return nil, err } @@ -176,7 +191,7 @@ func (c *jobSpecificationServiceClient) ListJobSpecification(ctx context.Context func (c *jobSpecificationServiceClient) CheckJobSpecification(ctx context.Context, in *CheckJobSpecificationRequest, opts ...grpc.CallOption) (*CheckJobSpecificationResponse, error) { out := new(CheckJobSpecificationResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.JobSpecificationService/CheckJobSpecification", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.JobSpecificationService/CheckJobSpecification", in, out, opts...) if err != nil { return nil, err } @@ -184,7 +199,7 @@ func (c *jobSpecificationServiceClient) CheckJobSpecification(ctx context.Contex } func (c *jobSpecificationServiceClient) CheckJobSpecifications(ctx context.Context, in *CheckJobSpecificationsRequest, opts ...grpc.CallOption) (JobSpecificationService_CheckJobSpecificationsClient, error) { - stream, err := c.cc.NewStream(ctx, &JobSpecificationService_ServiceDesc.Streams[1], "/odpf.optimus.core.v1beta1.JobSpecificationService/CheckJobSpecifications", opts...) + stream, err := c.cc.NewStream(ctx, &JobSpecificationService_ServiceDesc.Streams[1], "/raystack.optimus.core.v1beta1.JobSpecificationService/CheckJobSpecifications", opts...) if err != nil { return nil, err } @@ -216,7 +231,7 @@ func (x *jobSpecificationServiceCheckJobSpecificationsClient) Recv() (*CheckJobS } func (c *jobSpecificationServiceClient) RefreshJobs(ctx context.Context, in *RefreshJobsRequest, opts ...grpc.CallOption) (JobSpecificationService_RefreshJobsClient, error) { - stream, err := c.cc.NewStream(ctx, &JobSpecificationService_ServiceDesc.Streams[2], "/odpf.optimus.core.v1beta1.JobSpecificationService/RefreshJobs", opts...) + stream, err := c.cc.NewStream(ctx, &JobSpecificationService_ServiceDesc.Streams[2], "/raystack.optimus.core.v1beta1.JobSpecificationService/RefreshJobs", opts...) if err != nil { return nil, err } @@ -249,7 +264,7 @@ func (x *jobSpecificationServiceRefreshJobsClient) Recv() (*RefreshJobsResponse, func (c *jobSpecificationServiceClient) GetDeployJobsStatus(ctx context.Context, in *GetDeployJobsStatusRequest, opts ...grpc.CallOption) (*GetDeployJobsStatusResponse, error) { out := new(GetDeployJobsStatusResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.JobSpecificationService/GetDeployJobsStatus", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.JobSpecificationService/GetDeployJobsStatus", in, out, opts...) if err != nil { return nil, err } @@ -257,7 +272,7 @@ func (c *jobSpecificationServiceClient) GetDeployJobsStatus(ctx context.Context, } func (c *jobSpecificationServiceClient) ReplaceAllJobSpecifications(ctx context.Context, opts ...grpc.CallOption) (JobSpecificationService_ReplaceAllJobSpecificationsClient, error) { - stream, err := c.cc.NewStream(ctx, &JobSpecificationService_ServiceDesc.Streams[3], "/odpf.optimus.core.v1beta1.JobSpecificationService/ReplaceAllJobSpecifications", opts...) + stream, err := c.cc.NewStream(ctx, &JobSpecificationService_ServiceDesc.Streams[3], "/raystack.optimus.core.v1beta1.JobSpecificationService/ReplaceAllJobSpecifications", opts...) if err != nil { return nil, err } @@ -289,7 +304,7 @@ func (x *jobSpecificationServiceReplaceAllJobSpecificationsClient) Recv() (*Repl func (c *jobSpecificationServiceClient) GetJobTask(ctx context.Context, in *GetJobTaskRequest, opts ...grpc.CallOption) (*GetJobTaskResponse, error) { out := new(GetJobTaskResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.JobSpecificationService/GetJobTask", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.JobSpecificationService/GetJobTask", in, out, opts...) if err != nil { return nil, err } @@ -298,7 +313,25 @@ func (c *jobSpecificationServiceClient) GetJobTask(ctx context.Context, in *GetJ func (c *jobSpecificationServiceClient) GetWindow(ctx context.Context, in *GetWindowRequest, opts ...grpc.CallOption) (*GetWindowResponse, error) { out := new(GetWindowResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.JobSpecificationService/GetWindow", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.JobSpecificationService/GetWindow", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *jobSpecificationServiceClient) UpdateJobsState(ctx context.Context, in *UpdateJobsStateRequest, opts ...grpc.CallOption) (*UpdateJobsStateResponse, error) { + out := new(UpdateJobsStateResponse) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.JobSpecificationService/UpdateJobsState", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *jobSpecificationServiceClient) SyncJobsState(ctx context.Context, in *SyncJobsStateRequest, opts ...grpc.CallOption) (*SyncJobsStateResponse, error) { + out := new(SyncJobsStateResponse) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.JobSpecificationService/SyncJobsState", in, out, opts...) if err != nil { return nil, err } @@ -329,6 +362,8 @@ type JobSpecificationServiceServer interface { GetJobSpecifications(context.Context, *GetJobSpecificationsRequest) (*GetJobSpecificationsResponse, error) // DeleteJobSpecification deletes a job spec of a namespace DeleteJobSpecification(context.Context, *DeleteJobSpecificationRequest) (*DeleteJobSpecificationResponse, error) + // ChangeJobNamespace move a job spec from one namespace to another + ChangeJobNamespace(context.Context, *ChangeJobNamespaceRequest) (*ChangeJobNamespaceResponse, error) // ListJobSpecification returns list of jobs created in a project ListJobSpecification(context.Context, *ListJobSpecificationRequest) (*ListJobSpecificationResponse, error) // CheckJobSpecification checks if a job specification is valid @@ -348,6 +383,10 @@ type JobSpecificationServiceServer interface { // GetWindow provides the start and end dates provided a scheduled date // of the execution window GetWindow(context.Context, *GetWindowRequest) (*GetWindowResponse, error) + // UpdateJobState enable / disable job on scheuler + UpdateJobsState(context.Context, *UpdateJobsStateRequest) (*UpdateJobsStateResponse, error) + // SyncJobsState enable / disable job on scheuler + SyncJobsState(context.Context, *SyncJobsStateRequest) (*SyncJobsStateResponse, error) mustEmbedUnimplementedJobSpecificationServiceServer() } @@ -379,6 +418,9 @@ func (UnimplementedJobSpecificationServiceServer) GetJobSpecifications(context.C func (UnimplementedJobSpecificationServiceServer) DeleteJobSpecification(context.Context, *DeleteJobSpecificationRequest) (*DeleteJobSpecificationResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteJobSpecification not implemented") } +func (UnimplementedJobSpecificationServiceServer) ChangeJobNamespace(context.Context, *ChangeJobNamespaceRequest) (*ChangeJobNamespaceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ChangeJobNamespace not implemented") +} func (UnimplementedJobSpecificationServiceServer) ListJobSpecification(context.Context, *ListJobSpecificationRequest) (*ListJobSpecificationResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListJobSpecification not implemented") } @@ -403,6 +445,12 @@ func (UnimplementedJobSpecificationServiceServer) GetJobTask(context.Context, *G func (UnimplementedJobSpecificationServiceServer) GetWindow(context.Context, *GetWindowRequest) (*GetWindowResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetWindow not implemented") } +func (UnimplementedJobSpecificationServiceServer) UpdateJobsState(context.Context, *UpdateJobsStateRequest) (*UpdateJobsStateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateJobsState not implemented") +} +func (UnimplementedJobSpecificationServiceServer) SyncJobsState(context.Context, *SyncJobsStateRequest) (*SyncJobsStateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SyncJobsState not implemented") +} func (UnimplementedJobSpecificationServiceServer) mustEmbedUnimplementedJobSpecificationServiceServer() { } @@ -453,7 +501,7 @@ func _JobSpecificationService_JobInspect_Handler(srv interface{}, ctx context.Co } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.JobSpecificationService/JobInspect", + FullMethod: "/raystack.optimus.core.v1beta1.JobSpecificationService/JobInspect", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(JobSpecificationServiceServer).JobInspect(ctx, req.(*JobInspectRequest)) @@ -471,7 +519,7 @@ func _JobSpecificationService_CreateJobSpecification_Handler(srv interface{}, ct } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.JobSpecificationService/CreateJobSpecification", + FullMethod: "/raystack.optimus.core.v1beta1.JobSpecificationService/CreateJobSpecification", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(JobSpecificationServiceServer).CreateJobSpecification(ctx, req.(*CreateJobSpecificationRequest)) @@ -489,7 +537,7 @@ func _JobSpecificationService_AddJobSpecifications_Handler(srv interface{}, ctx } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.JobSpecificationService/AddJobSpecifications", + FullMethod: "/raystack.optimus.core.v1beta1.JobSpecificationService/AddJobSpecifications", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(JobSpecificationServiceServer).AddJobSpecifications(ctx, req.(*AddJobSpecificationsRequest)) @@ -507,7 +555,7 @@ func _JobSpecificationService_UpdateJobSpecifications_Handler(srv interface{}, c } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.JobSpecificationService/UpdateJobSpecifications", + FullMethod: "/raystack.optimus.core.v1beta1.JobSpecificationService/UpdateJobSpecifications", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(JobSpecificationServiceServer).UpdateJobSpecifications(ctx, req.(*UpdateJobSpecificationsRequest)) @@ -525,7 +573,7 @@ func _JobSpecificationService_GetJobSpecification_Handler(srv interface{}, ctx c } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.JobSpecificationService/GetJobSpecification", + FullMethod: "/raystack.optimus.core.v1beta1.JobSpecificationService/GetJobSpecification", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(JobSpecificationServiceServer).GetJobSpecification(ctx, req.(*GetJobSpecificationRequest)) @@ -543,7 +591,7 @@ func _JobSpecificationService_GetJobSpecifications_Handler(srv interface{}, ctx } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.JobSpecificationService/GetJobSpecifications", + FullMethod: "/raystack.optimus.core.v1beta1.JobSpecificationService/GetJobSpecifications", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(JobSpecificationServiceServer).GetJobSpecifications(ctx, req.(*GetJobSpecificationsRequest)) @@ -561,7 +609,7 @@ func _JobSpecificationService_DeleteJobSpecification_Handler(srv interface{}, ct } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.JobSpecificationService/DeleteJobSpecification", + FullMethod: "/raystack.optimus.core.v1beta1.JobSpecificationService/DeleteJobSpecification", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(JobSpecificationServiceServer).DeleteJobSpecification(ctx, req.(*DeleteJobSpecificationRequest)) @@ -569,6 +617,24 @@ func _JobSpecificationService_DeleteJobSpecification_Handler(srv interface{}, ct return interceptor(ctx, in, info, handler) } +func _JobSpecificationService_ChangeJobNamespace_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ChangeJobNamespaceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(JobSpecificationServiceServer).ChangeJobNamespace(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/raystack.optimus.core.v1beta1.JobSpecificationService/ChangeJobNamespace", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(JobSpecificationServiceServer).ChangeJobNamespace(ctx, req.(*ChangeJobNamespaceRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _JobSpecificationService_ListJobSpecification_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ListJobSpecificationRequest) if err := dec(in); err != nil { @@ -579,7 +645,7 @@ func _JobSpecificationService_ListJobSpecification_Handler(srv interface{}, ctx } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.JobSpecificationService/ListJobSpecification", + FullMethod: "/raystack.optimus.core.v1beta1.JobSpecificationService/ListJobSpecification", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(JobSpecificationServiceServer).ListJobSpecification(ctx, req.(*ListJobSpecificationRequest)) @@ -597,7 +663,7 @@ func _JobSpecificationService_CheckJobSpecification_Handler(srv interface{}, ctx } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.JobSpecificationService/CheckJobSpecification", + FullMethod: "/raystack.optimus.core.v1beta1.JobSpecificationService/CheckJobSpecification", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(JobSpecificationServiceServer).CheckJobSpecification(ctx, req.(*CheckJobSpecificationRequest)) @@ -657,7 +723,7 @@ func _JobSpecificationService_GetDeployJobsStatus_Handler(srv interface{}, ctx c } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.JobSpecificationService/GetDeployJobsStatus", + FullMethod: "/raystack.optimus.core.v1beta1.JobSpecificationService/GetDeployJobsStatus", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(JobSpecificationServiceServer).GetDeployJobsStatus(ctx, req.(*GetDeployJobsStatusRequest)) @@ -701,7 +767,7 @@ func _JobSpecificationService_GetJobTask_Handler(srv interface{}, ctx context.Co } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.JobSpecificationService/GetJobTask", + FullMethod: "/raystack.optimus.core.v1beta1.JobSpecificationService/GetJobTask", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(JobSpecificationServiceServer).GetJobTask(ctx, req.(*GetJobTaskRequest)) @@ -719,7 +785,7 @@ func _JobSpecificationService_GetWindow_Handler(srv interface{}, ctx context.Con } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.JobSpecificationService/GetWindow", + FullMethod: "/raystack.optimus.core.v1beta1.JobSpecificationService/GetWindow", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(JobSpecificationServiceServer).GetWindow(ctx, req.(*GetWindowRequest)) @@ -727,11 +793,47 @@ func _JobSpecificationService_GetWindow_Handler(srv interface{}, ctx context.Con return interceptor(ctx, in, info, handler) } +func _JobSpecificationService_UpdateJobsState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateJobsStateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(JobSpecificationServiceServer).UpdateJobsState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/raystack.optimus.core.v1beta1.JobSpecificationService/UpdateJobsState", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(JobSpecificationServiceServer).UpdateJobsState(ctx, req.(*UpdateJobsStateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _JobSpecificationService_SyncJobsState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SyncJobsStateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(JobSpecificationServiceServer).SyncJobsState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/raystack.optimus.core.v1beta1.JobSpecificationService/SyncJobsState", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(JobSpecificationServiceServer).SyncJobsState(ctx, req.(*SyncJobsStateRequest)) + } + return interceptor(ctx, in, info, handler) +} + // JobSpecificationService_ServiceDesc is the grpc.ServiceDesc for JobSpecificationService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var JobSpecificationService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "odpf.optimus.core.v1beta1.JobSpecificationService", + ServiceName: "raystack.optimus.core.v1beta1.JobSpecificationService", HandlerType: (*JobSpecificationServiceServer)(nil), Methods: []grpc.MethodDesc{ { @@ -762,6 +864,10 @@ var JobSpecificationService_ServiceDesc = grpc.ServiceDesc{ MethodName: "DeleteJobSpecification", Handler: _JobSpecificationService_DeleteJobSpecification_Handler, }, + { + MethodName: "ChangeJobNamespace", + Handler: _JobSpecificationService_ChangeJobNamespace_Handler, + }, { MethodName: "ListJobSpecification", Handler: _JobSpecificationService_ListJobSpecification_Handler, @@ -782,6 +888,14 @@ var JobSpecificationService_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetWindow", Handler: _JobSpecificationService_GetWindow_Handler, }, + { + MethodName: "UpdateJobsState", + Handler: _JobSpecificationService_UpdateJobsState_Handler, + }, + { + MethodName: "SyncJobsState", + Handler: _JobSpecificationService_SyncJobsState_Handler, + }, }, Streams: []grpc.StreamDesc{ { @@ -807,5 +921,5 @@ var JobSpecificationService_ServiceDesc = grpc.ServiceDesc{ ClientStreams: true, }, }, - Metadata: "odpf/optimus/core/v1beta1/job_spec.proto", + Metadata: "raystack/optimus/core/v1beta1/job_spec.proto", } diff --git a/protos/raystack/optimus/core/v1beta1/namespace.pb.go b/protos/raystack/optimus/core/v1beta1/namespace.pb.go new file mode 100644 index 0000000000..c0e7540040 --- /dev/null +++ b/protos/raystack/optimus/core/v1beta1/namespace.pb.go @@ -0,0 +1,655 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.0 +// protoc (unknown) +// source: raystack/optimus/core/v1beta1/namespace.proto + +package optimus + +import ( + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type RegisterProjectNamespaceRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + Namespace *NamespaceSpecification `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"` +} + +func (x *RegisterProjectNamespaceRequest) Reset() { + *x = RegisterProjectNamespaceRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_namespace_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisterProjectNamespaceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterProjectNamespaceRequest) ProtoMessage() {} + +func (x *RegisterProjectNamespaceRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_namespace_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterProjectNamespaceRequest.ProtoReflect.Descriptor instead. +func (*RegisterProjectNamespaceRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_namespace_proto_rawDescGZIP(), []int{0} +} + +func (x *RegisterProjectNamespaceRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *RegisterProjectNamespaceRequest) GetNamespace() *NamespaceSpecification { + if x != nil { + return x.Namespace + } + return nil +} + +type RegisterProjectNamespaceResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *RegisterProjectNamespaceResponse) Reset() { + *x = RegisterProjectNamespaceResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_namespace_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisterProjectNamespaceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterProjectNamespaceResponse) ProtoMessage() {} + +func (x *RegisterProjectNamespaceResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_namespace_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterProjectNamespaceResponse.ProtoReflect.Descriptor instead. +func (*RegisterProjectNamespaceResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_namespace_proto_rawDescGZIP(), []int{1} +} + +func (x *RegisterProjectNamespaceResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +func (x *RegisterProjectNamespaceResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +type ListProjectNamespacesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` +} + +func (x *ListProjectNamespacesRequest) Reset() { + *x = ListProjectNamespacesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_namespace_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListProjectNamespacesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListProjectNamespacesRequest) ProtoMessage() {} + +func (x *ListProjectNamespacesRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_namespace_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListProjectNamespacesRequest.ProtoReflect.Descriptor instead. +func (*ListProjectNamespacesRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_namespace_proto_rawDescGZIP(), []int{2} +} + +func (x *ListProjectNamespacesRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +type ListProjectNamespacesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Namespaces []*NamespaceSpecification `protobuf:"bytes,1,rep,name=namespaces,proto3" json:"namespaces,omitempty"` +} + +func (x *ListProjectNamespacesResponse) Reset() { + *x = ListProjectNamespacesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_namespace_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListProjectNamespacesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListProjectNamespacesResponse) ProtoMessage() {} + +func (x *ListProjectNamespacesResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_namespace_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListProjectNamespacesResponse.ProtoReflect.Descriptor instead. +func (*ListProjectNamespacesResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_namespace_proto_rawDescGZIP(), []int{3} +} + +func (x *ListProjectNamespacesResponse) GetNamespaces() []*NamespaceSpecification { + if x != nil { + return x.Namespaces + } + return nil +} + +type GetNamespaceRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + NamespaceName string `protobuf:"bytes,2,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` +} + +func (x *GetNamespaceRequest) Reset() { + *x = GetNamespaceRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_namespace_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetNamespaceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetNamespaceRequest) ProtoMessage() {} + +func (x *GetNamespaceRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_namespace_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetNamespaceRequest.ProtoReflect.Descriptor instead. +func (*GetNamespaceRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_namespace_proto_rawDescGZIP(), []int{4} +} + +func (x *GetNamespaceRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *GetNamespaceRequest) GetNamespaceName() string { + if x != nil { + return x.NamespaceName + } + return "" +} + +type GetNamespaceResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Namespace *NamespaceSpecification `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` +} + +func (x *GetNamespaceResponse) Reset() { + *x = GetNamespaceResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_namespace_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetNamespaceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetNamespaceResponse) ProtoMessage() {} + +func (x *GetNamespaceResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_namespace_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetNamespaceResponse.ProtoReflect.Descriptor instead. +func (*GetNamespaceResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_namespace_proto_rawDescGZIP(), []int{5} +} + +func (x *GetNamespaceResponse) GetNamespace() *NamespaceSpecification { + if x != nil { + return x.Namespace + } + return nil +} + +type NamespaceSpecification struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Config map[string]string `protobuf:"bytes,2,rep,name=config,proto3" json:"config,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *NamespaceSpecification) Reset() { + *x = NamespaceSpecification{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_namespace_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NamespaceSpecification) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NamespaceSpecification) ProtoMessage() {} + +func (x *NamespaceSpecification) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_namespace_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NamespaceSpecification.ProtoReflect.Descriptor instead. +func (*NamespaceSpecification) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_namespace_proto_rawDescGZIP(), []int{6} +} + +func (x *NamespaceSpecification) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *NamespaceSpecification) GetConfig() map[string]string { + if x != nil { + return x.Config + } + return nil +} + +var File_raystack_optimus_core_v1beta1_namespace_proto protoreflect.FileDescriptor + +var file_raystack_optimus_core_v1beta1_namespace_proto_rawDesc = []byte{ + 0x0a, 0x30, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2f, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x20, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, + 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, + 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x22, 0x9c, 0x01, 0x0a, 0x1f, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x56, 0x0a, 0x09, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x67, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x22, 0x56, 0x0a, 0x20, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, + 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x41, 0x0a, 0x1c, 0x4c, 0x69, 0x73, + 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x79, 0x0a, 0x1d, + 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, + 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x38, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, + 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x70, + 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x22, 0x5f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, + 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x6e, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x4e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x56, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, + 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0xc5, 0x01, 0x0a, 0x16, 0x4e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x5c, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x44, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x39, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x32, 0xfe, 0x04, 0x0a, 0x10, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xd7, 0x01, 0x0a, 0x18, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x12, 0x41, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x42, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x34, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x2e, 0x22, 0x29, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x3a, 0x01, 0x2a, 0x12, + 0xcb, 0x01, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x3e, 0x2e, 0x67, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3f, 0x2e, 0x67, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x2b, 0x12, 0x29, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0xc1, 0x01, + 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x35, + 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x42, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x3c, 0x12, 0x3a, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x7d, 0x42, 0x9b, 0x01, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2e, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x42, 0x17, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x50, 0x01, 0x5a, + 0x1e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x74, 0x6f, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x92, + 0x41, 0x3d, 0x12, 0x05, 0x32, 0x03, 0x30, 0x2e, 0x31, 0x1a, 0x0e, 0x31, 0x32, 0x37, 0x2e, 0x30, + 0x2e, 0x30, 0x2e, 0x31, 0x3a, 0x39, 0x31, 0x30, 0x30, 0x22, 0x04, 0x2f, 0x61, 0x70, 0x69, 0x2a, + 0x01, 0x01, 0x72, 0x1b, 0x0a, 0x19, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x20, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_raystack_optimus_core_v1beta1_namespace_proto_rawDescOnce sync.Once + file_raystack_optimus_core_v1beta1_namespace_proto_rawDescData = file_raystack_optimus_core_v1beta1_namespace_proto_rawDesc +) + +func file_raystack_optimus_core_v1beta1_namespace_proto_rawDescGZIP() []byte { + file_raystack_optimus_core_v1beta1_namespace_proto_rawDescOnce.Do(func() { + file_raystack_optimus_core_v1beta1_namespace_proto_rawDescData = protoimpl.X.CompressGZIP(file_raystack_optimus_core_v1beta1_namespace_proto_rawDescData) + }) + return file_raystack_optimus_core_v1beta1_namespace_proto_rawDescData +} + +var file_raystack_optimus_core_v1beta1_namespace_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_raystack_optimus_core_v1beta1_namespace_proto_goTypes = []interface{}{ + (*RegisterProjectNamespaceRequest)(nil), // 0: raystack.optimus.core.v1beta1.RegisterProjectNamespaceRequest + (*RegisterProjectNamespaceResponse)(nil), // 1: raystack.optimus.core.v1beta1.RegisterProjectNamespaceResponse + (*ListProjectNamespacesRequest)(nil), // 2: raystack.optimus.core.v1beta1.ListProjectNamespacesRequest + (*ListProjectNamespacesResponse)(nil), // 3: raystack.optimus.core.v1beta1.ListProjectNamespacesResponse + (*GetNamespaceRequest)(nil), // 4: raystack.optimus.core.v1beta1.GetNamespaceRequest + (*GetNamespaceResponse)(nil), // 5: raystack.optimus.core.v1beta1.GetNamespaceResponse + (*NamespaceSpecification)(nil), // 6: raystack.optimus.core.v1beta1.NamespaceSpecification + nil, // 7: raystack.optimus.core.v1beta1.NamespaceSpecification.ConfigEntry +} +var file_raystack_optimus_core_v1beta1_namespace_proto_depIdxs = []int32{ + 6, // 0: raystack.optimus.core.v1beta1.RegisterProjectNamespaceRequest.namespace:type_name -> raystack.optimus.core.v1beta1.NamespaceSpecification + 6, // 1: raystack.optimus.core.v1beta1.ListProjectNamespacesResponse.namespaces:type_name -> raystack.optimus.core.v1beta1.NamespaceSpecification + 6, // 2: raystack.optimus.core.v1beta1.GetNamespaceResponse.namespace:type_name -> raystack.optimus.core.v1beta1.NamespaceSpecification + 7, // 3: raystack.optimus.core.v1beta1.NamespaceSpecification.config:type_name -> raystack.optimus.core.v1beta1.NamespaceSpecification.ConfigEntry + 0, // 4: raystack.optimus.core.v1beta1.NamespaceService.RegisterProjectNamespace:input_type -> raystack.optimus.core.v1beta1.RegisterProjectNamespaceRequest + 2, // 5: raystack.optimus.core.v1beta1.NamespaceService.ListProjectNamespaces:input_type -> raystack.optimus.core.v1beta1.ListProjectNamespacesRequest + 4, // 6: raystack.optimus.core.v1beta1.NamespaceService.GetNamespace:input_type -> raystack.optimus.core.v1beta1.GetNamespaceRequest + 1, // 7: raystack.optimus.core.v1beta1.NamespaceService.RegisterProjectNamespace:output_type -> raystack.optimus.core.v1beta1.RegisterProjectNamespaceResponse + 3, // 8: raystack.optimus.core.v1beta1.NamespaceService.ListProjectNamespaces:output_type -> raystack.optimus.core.v1beta1.ListProjectNamespacesResponse + 5, // 9: raystack.optimus.core.v1beta1.NamespaceService.GetNamespace:output_type -> raystack.optimus.core.v1beta1.GetNamespaceResponse + 7, // [7:10] is the sub-list for method output_type + 4, // [4:7] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name +} + +func init() { file_raystack_optimus_core_v1beta1_namespace_proto_init() } +func file_raystack_optimus_core_v1beta1_namespace_proto_init() { + if File_raystack_optimus_core_v1beta1_namespace_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_raystack_optimus_core_v1beta1_namespace_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterProjectNamespaceRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_namespace_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterProjectNamespaceResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_namespace_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListProjectNamespacesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_namespace_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListProjectNamespacesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_namespace_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetNamespaceRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_namespace_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetNamespaceResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_namespace_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NamespaceSpecification); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_raystack_optimus_core_v1beta1_namespace_proto_rawDesc, + NumEnums: 0, + NumMessages: 8, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_raystack_optimus_core_v1beta1_namespace_proto_goTypes, + DependencyIndexes: file_raystack_optimus_core_v1beta1_namespace_proto_depIdxs, + MessageInfos: file_raystack_optimus_core_v1beta1_namespace_proto_msgTypes, + }.Build() + File_raystack_optimus_core_v1beta1_namespace_proto = out.File + file_raystack_optimus_core_v1beta1_namespace_proto_rawDesc = nil + file_raystack_optimus_core_v1beta1_namespace_proto_goTypes = nil + file_raystack_optimus_core_v1beta1_namespace_proto_depIdxs = nil +} diff --git a/protos/odpf/optimus/core/v1beta1/namespace.pb.gw.go b/protos/raystack/optimus/core/v1beta1/namespace.pb.gw.go similarity index 93% rename from protos/odpf/optimus/core/v1beta1/namespace.pb.gw.go rename to protos/raystack/optimus/core/v1beta1/namespace.pb.gw.go index 4b013dc4bd..1c170efdb8 100644 --- a/protos/odpf/optimus/core/v1beta1/namespace.pb.gw.go +++ b/protos/raystack/optimus/core/v1beta1/namespace.pb.gw.go @@ -1,5 +1,5 @@ // Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. -// source: odpf/optimus/core/v1beta1/namespace.proto +// source: raystack/optimus/core/v1beta1/namespace.proto /* Package optimus is a reverse proxy. @@ -235,7 +235,7 @@ func RegisterNamespaceServiceHandlerServer(ctx context.Context, mux *runtime.Ser var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.NamespaceService/RegisterProjectNamespace", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.NamespaceService/RegisterProjectNamespace", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -258,7 +258,7 @@ func RegisterNamespaceServiceHandlerServer(ctx context.Context, mux *runtime.Ser var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.NamespaceService/ListProjectNamespaces", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.NamespaceService/ListProjectNamespaces", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -281,7 +281,7 @@ func RegisterNamespaceServiceHandlerServer(ctx context.Context, mux *runtime.Ser var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.NamespaceService/GetNamespace", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.NamespaceService/GetNamespace", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -343,7 +343,7 @@ func RegisterNamespaceServiceHandlerClient(ctx context.Context, mux *runtime.Ser ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.NamespaceService/RegisterProjectNamespace", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.NamespaceService/RegisterProjectNamespace", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -363,7 +363,7 @@ func RegisterNamespaceServiceHandlerClient(ctx context.Context, mux *runtime.Ser ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.NamespaceService/ListProjectNamespaces", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.NamespaceService/ListProjectNamespaces", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -383,7 +383,7 @@ func RegisterNamespaceServiceHandlerClient(ctx context.Context, mux *runtime.Ser ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.NamespaceService/GetNamespace", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.NamespaceService/GetNamespace", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return diff --git a/protos/odpf/optimus/core/v1beta1/namespace.swagger.json b/protos/raystack/optimus/core/v1beta1/namespace.swagger.json similarity index 93% rename from protos/odpf/optimus/core/v1beta1/namespace.swagger.json rename to protos/raystack/optimus/core/v1beta1/namespace.swagger.json index 93b196bff8..afe9bb6c57 100644 --- a/protos/odpf/optimus/core/v1beta1/namespace.swagger.json +++ b/protos/raystack/optimus/core/v1beta1/namespace.swagger.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "title": "odpf/optimus/core/v1beta1/namespace.proto", + "title": "raystack/optimus/core/v1beta1/namespace.proto", "version": "0.1" }, "tags": [ @@ -11,15 +11,9 @@ ], "host": "127.0.0.1:9100", "basePath": "/api", - "schemes": [ - "http" - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], + "schemes": ["http"], + "consumes": ["application/json"], + "produces": ["application/json"], "paths": { "/v1beta1/project/{projectName}/namespace": { "get": { @@ -47,9 +41,7 @@ "type": "string" } ], - "tags": [ - "NamespaceService" - ] + "tags": ["NamespaceService"] }, "post": { "summary": "RegisterProjectNamespace creates a new namespace for a project", @@ -89,9 +81,7 @@ } } ], - "tags": [ - "NamespaceService" - ] + "tags": ["NamespaceService"] } }, "/v1beta1/project/{projectName}/namespace/{namespaceName}": { @@ -126,9 +116,7 @@ "type": "string" } ], - "tags": [ - "NamespaceService" - ] + "tags": ["NamespaceService"] } } }, diff --git a/protos/odpf/optimus/core/v1beta1/namespace_grpc.pb.go b/protos/raystack/optimus/core/v1beta1/namespace_grpc.pb.go similarity index 90% rename from protos/odpf/optimus/core/v1beta1/namespace_grpc.pb.go rename to protos/raystack/optimus/core/v1beta1/namespace_grpc.pb.go index b4d70abb1d..fa7da1091f 100644 --- a/protos/odpf/optimus/core/v1beta1/namespace_grpc.pb.go +++ b/protos/raystack/optimus/core/v1beta1/namespace_grpc.pb.go @@ -2,7 +2,7 @@ // versions: // - protoc-gen-go-grpc v1.2.0 // - protoc (unknown) -// source: odpf/optimus/core/v1beta1/namespace.proto +// source: raystack/optimus/core/v1beta1/namespace.proto package optimus @@ -40,7 +40,7 @@ func NewNamespaceServiceClient(cc grpc.ClientConnInterface) NamespaceServiceClie func (c *namespaceServiceClient) RegisterProjectNamespace(ctx context.Context, in *RegisterProjectNamespaceRequest, opts ...grpc.CallOption) (*RegisterProjectNamespaceResponse, error) { out := new(RegisterProjectNamespaceResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.NamespaceService/RegisterProjectNamespace", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.NamespaceService/RegisterProjectNamespace", in, out, opts...) if err != nil { return nil, err } @@ -49,7 +49,7 @@ func (c *namespaceServiceClient) RegisterProjectNamespace(ctx context.Context, i func (c *namespaceServiceClient) ListProjectNamespaces(ctx context.Context, in *ListProjectNamespacesRequest, opts ...grpc.CallOption) (*ListProjectNamespacesResponse, error) { out := new(ListProjectNamespacesResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.NamespaceService/ListProjectNamespaces", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.NamespaceService/ListProjectNamespaces", in, out, opts...) if err != nil { return nil, err } @@ -58,7 +58,7 @@ func (c *namespaceServiceClient) ListProjectNamespaces(ctx context.Context, in * func (c *namespaceServiceClient) GetNamespace(ctx context.Context, in *GetNamespaceRequest, opts ...grpc.CallOption) (*GetNamespaceResponse, error) { out := new(GetNamespaceResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.NamespaceService/GetNamespace", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.NamespaceService/GetNamespace", in, out, opts...) if err != nil { return nil, err } @@ -114,7 +114,7 @@ func _NamespaceService_RegisterProjectNamespace_Handler(srv interface{}, ctx con } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.NamespaceService/RegisterProjectNamespace", + FullMethod: "/raystack.optimus.core.v1beta1.NamespaceService/RegisterProjectNamespace", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(NamespaceServiceServer).RegisterProjectNamespace(ctx, req.(*RegisterProjectNamespaceRequest)) @@ -132,7 +132,7 @@ func _NamespaceService_ListProjectNamespaces_Handler(srv interface{}, ctx contex } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.NamespaceService/ListProjectNamespaces", + FullMethod: "/raystack.optimus.core.v1beta1.NamespaceService/ListProjectNamespaces", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(NamespaceServiceServer).ListProjectNamespaces(ctx, req.(*ListProjectNamespacesRequest)) @@ -150,7 +150,7 @@ func _NamespaceService_GetNamespace_Handler(srv interface{}, ctx context.Context } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.NamespaceService/GetNamespace", + FullMethod: "/raystack.optimus.core.v1beta1.NamespaceService/GetNamespace", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(NamespaceServiceServer).GetNamespace(ctx, req.(*GetNamespaceRequest)) @@ -162,7 +162,7 @@ func _NamespaceService_GetNamespace_Handler(srv interface{}, ctx context.Context // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var NamespaceService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "odpf.optimus.core.v1beta1.NamespaceService", + ServiceName: "raystack.optimus.core.v1beta1.NamespaceService", HandlerType: (*NamespaceServiceServer)(nil), Methods: []grpc.MethodDesc{ { @@ -179,5 +179,5 @@ var NamespaceService_ServiceDesc = grpc.ServiceDesc{ }, }, Streams: []grpc.StreamDesc{}, - Metadata: "odpf/optimus/core/v1beta1/namespace.proto", + Metadata: "raystack/optimus/core/v1beta1/namespace.proto", } diff --git a/protos/raystack/optimus/core/v1beta1/project.pb.go b/protos/raystack/optimus/core/v1beta1/project.pb.go new file mode 100644 index 0000000000..021cccba14 --- /dev/null +++ b/protos/raystack/optimus/core/v1beta1/project.pb.go @@ -0,0 +1,697 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.0 +// protoc (unknown) +// source: raystack/optimus/core/v1beta1/project.proto + +package optimus + +import ( + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type RegisterProjectRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Project *ProjectSpecification `protobuf:"bytes,1,opt,name=project,proto3" json:"project,omitempty"` +} + +func (x *RegisterProjectRequest) Reset() { + *x = RegisterProjectRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_project_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisterProjectRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterProjectRequest) ProtoMessage() {} + +func (x *RegisterProjectRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_project_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterProjectRequest.ProtoReflect.Descriptor instead. +func (*RegisterProjectRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_project_proto_rawDescGZIP(), []int{0} +} + +func (x *RegisterProjectRequest) GetProject() *ProjectSpecification { + if x != nil { + return x.Project + } + return nil +} + +type RegisterProjectResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *RegisterProjectResponse) Reset() { + *x = RegisterProjectResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_project_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisterProjectResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterProjectResponse) ProtoMessage() {} + +func (x *RegisterProjectResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_project_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterProjectResponse.ProtoReflect.Descriptor instead. +func (*RegisterProjectResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_project_proto_rawDescGZIP(), []int{1} +} + +func (x *RegisterProjectResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +func (x *RegisterProjectResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +type ListProjectsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ListProjectsRequest) Reset() { + *x = ListProjectsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_project_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListProjectsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListProjectsRequest) ProtoMessage() {} + +func (x *ListProjectsRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_project_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListProjectsRequest.ProtoReflect.Descriptor instead. +func (*ListProjectsRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_project_proto_rawDescGZIP(), []int{2} +} + +type ListProjectsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Projects []*ProjectSpecification `protobuf:"bytes,1,rep,name=projects,proto3" json:"projects,omitempty"` +} + +func (x *ListProjectsResponse) Reset() { + *x = ListProjectsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_project_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListProjectsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListProjectsResponse) ProtoMessage() {} + +func (x *ListProjectsResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_project_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListProjectsResponse.ProtoReflect.Descriptor instead. +func (*ListProjectsResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_project_proto_rawDescGZIP(), []int{3} +} + +func (x *ListProjectsResponse) GetProjects() []*ProjectSpecification { + if x != nil { + return x.Projects + } + return nil +} + +type GetProjectRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` +} + +func (x *GetProjectRequest) Reset() { + *x = GetProjectRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_project_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetProjectRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetProjectRequest) ProtoMessage() {} + +func (x *GetProjectRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_project_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetProjectRequest.ProtoReflect.Descriptor instead. +func (*GetProjectRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_project_proto_rawDescGZIP(), []int{4} +} + +func (x *GetProjectRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +type GetProjectResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Project *ProjectSpecification `protobuf:"bytes,1,opt,name=project,proto3" json:"project,omitempty"` +} + +func (x *GetProjectResponse) Reset() { + *x = GetProjectResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_project_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetProjectResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetProjectResponse) ProtoMessage() {} + +func (x *GetProjectResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_project_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetProjectResponse.ProtoReflect.Descriptor instead. +func (*GetProjectResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_project_proto_rawDescGZIP(), []int{5} +} + +func (x *GetProjectResponse) GetProject() *ProjectSpecification { + if x != nil { + return x.Project + } + return nil +} + +type ProjectSpecification struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Config map[string]string `protobuf:"bytes,2,rep,name=config,proto3" json:"config,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Secrets []*ProjectSpecification_ProjectSecret `protobuf:"bytes,3,rep,name=secrets,proto3" json:"secrets,omitempty"` +} + +func (x *ProjectSpecification) Reset() { + *x = ProjectSpecification{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_project_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProjectSpecification) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProjectSpecification) ProtoMessage() {} + +func (x *ProjectSpecification) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_project_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProjectSpecification.ProtoReflect.Descriptor instead. +func (*ProjectSpecification) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_project_proto_rawDescGZIP(), []int{6} +} + +func (x *ProjectSpecification) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ProjectSpecification) GetConfig() map[string]string { + if x != nil { + return x.Config + } + return nil +} + +func (x *ProjectSpecification) GetSecrets() []*ProjectSpecification_ProjectSecret { + if x != nil { + return x.Secrets + } + return nil +} + +type ProjectSpecification_ProjectSecret struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *ProjectSpecification_ProjectSecret) Reset() { + *x = ProjectSpecification_ProjectSecret{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_project_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProjectSpecification_ProjectSecret) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProjectSpecification_ProjectSecret) ProtoMessage() {} + +func (x *ProjectSpecification_ProjectSecret) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_project_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProjectSpecification_ProjectSecret.ProtoReflect.Descriptor instead. +func (*ProjectSpecification_ProjectSecret) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_project_proto_rawDescGZIP(), []int{6, 1} +} + +func (x *ProjectSpecification_ProjectSecret) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ProjectSpecification_ProjectSecret) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +var File_raystack_optimus_core_v1beta1_project_proto protoreflect.FileDescriptor + +var file_raystack_optimus_core_v1beta1_project_proto_rawDesc = []byte{ + 0x0a, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2f, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x20, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, + 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, + 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, + 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x22, 0x70, 0x0a, 0x16, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x50, 0x0a, 0x07, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x67, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, + 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4a, 0x04, 0x08, 0x02, + 0x10, 0x03, 0x22, 0x4d, 0x0a, 0x17, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, + 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, + 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x22, 0x15, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x6a, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, + 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x52, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x70, 0x65, + 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x73, 0x22, 0x36, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x66, 0x0a, 0x12, + 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x50, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, + 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x70, + 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x22, 0xdc, 0x02, 0x0a, 0x14, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x5a, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x42, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, + 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x70, 0x65, 0x63, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x5e, 0x0a, + 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x44, + 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, + 0x63, 0x72, 0x65, 0x74, 0x52, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x1a, 0x39, 0x0a, + 0x0b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x39, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x32, 0xf3, 0x03, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xa3, 0x01, 0x0a, 0x0f, 0x52, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x38, 0x2e, 0x67, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, + 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0x97, 0x01, 0x0a, + 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x35, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, + 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0xa0, 0x01, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x50, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x33, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x67, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, + 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, + 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x27, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x21, 0x12, 0x1f, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x42, 0x97, 0x01, 0x0a, 0x1e, 0x63, 0x6f, + 0x6d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x6e, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x42, 0x15, 0x50, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x1e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x67, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x92, 0x41, 0x3b, 0x12, 0x05, 0x32, 0x03, 0x30, 0x2e, 0x31, 0x1a, + 0x0e, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x3a, 0x39, 0x31, 0x30, 0x30, 0x22, + 0x04, 0x2f, 0x61, 0x70, 0x69, 0x2a, 0x01, 0x01, 0x72, 0x19, 0x0a, 0x17, 0x4f, 0x70, 0x74, 0x69, + 0x6d, 0x75, 0x73, 0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_raystack_optimus_core_v1beta1_project_proto_rawDescOnce sync.Once + file_raystack_optimus_core_v1beta1_project_proto_rawDescData = file_raystack_optimus_core_v1beta1_project_proto_rawDesc +) + +func file_raystack_optimus_core_v1beta1_project_proto_rawDescGZIP() []byte { + file_raystack_optimus_core_v1beta1_project_proto_rawDescOnce.Do(func() { + file_raystack_optimus_core_v1beta1_project_proto_rawDescData = protoimpl.X.CompressGZIP(file_raystack_optimus_core_v1beta1_project_proto_rawDescData) + }) + return file_raystack_optimus_core_v1beta1_project_proto_rawDescData +} + +var file_raystack_optimus_core_v1beta1_project_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_raystack_optimus_core_v1beta1_project_proto_goTypes = []interface{}{ + (*RegisterProjectRequest)(nil), // 0: raystack.optimus.core.v1beta1.RegisterProjectRequest + (*RegisterProjectResponse)(nil), // 1: raystack.optimus.core.v1beta1.RegisterProjectResponse + (*ListProjectsRequest)(nil), // 2: raystack.optimus.core.v1beta1.ListProjectsRequest + (*ListProjectsResponse)(nil), // 3: raystack.optimus.core.v1beta1.ListProjectsResponse + (*GetProjectRequest)(nil), // 4: raystack.optimus.core.v1beta1.GetProjectRequest + (*GetProjectResponse)(nil), // 5: raystack.optimus.core.v1beta1.GetProjectResponse + (*ProjectSpecification)(nil), // 6: raystack.optimus.core.v1beta1.ProjectSpecification + nil, // 7: raystack.optimus.core.v1beta1.ProjectSpecification.ConfigEntry + (*ProjectSpecification_ProjectSecret)(nil), // 8: raystack.optimus.core.v1beta1.ProjectSpecification.ProjectSecret +} +var file_raystack_optimus_core_v1beta1_project_proto_depIdxs = []int32{ + 6, // 0: raystack.optimus.core.v1beta1.RegisterProjectRequest.project:type_name -> raystack.optimus.core.v1beta1.ProjectSpecification + 6, // 1: raystack.optimus.core.v1beta1.ListProjectsResponse.projects:type_name -> raystack.optimus.core.v1beta1.ProjectSpecification + 6, // 2: raystack.optimus.core.v1beta1.GetProjectResponse.project:type_name -> raystack.optimus.core.v1beta1.ProjectSpecification + 7, // 3: raystack.optimus.core.v1beta1.ProjectSpecification.config:type_name -> raystack.optimus.core.v1beta1.ProjectSpecification.ConfigEntry + 8, // 4: raystack.optimus.core.v1beta1.ProjectSpecification.secrets:type_name -> raystack.optimus.core.v1beta1.ProjectSpecification.ProjectSecret + 0, // 5: raystack.optimus.core.v1beta1.ProjectService.RegisterProject:input_type -> raystack.optimus.core.v1beta1.RegisterProjectRequest + 2, // 6: raystack.optimus.core.v1beta1.ProjectService.ListProjects:input_type -> raystack.optimus.core.v1beta1.ListProjectsRequest + 4, // 7: raystack.optimus.core.v1beta1.ProjectService.GetProject:input_type -> raystack.optimus.core.v1beta1.GetProjectRequest + 1, // 8: raystack.optimus.core.v1beta1.ProjectService.RegisterProject:output_type -> raystack.optimus.core.v1beta1.RegisterProjectResponse + 3, // 9: raystack.optimus.core.v1beta1.ProjectService.ListProjects:output_type -> raystack.optimus.core.v1beta1.ListProjectsResponse + 5, // 10: raystack.optimus.core.v1beta1.ProjectService.GetProject:output_type -> raystack.optimus.core.v1beta1.GetProjectResponse + 8, // [8:11] is the sub-list for method output_type + 5, // [5:8] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name +} + +func init() { file_raystack_optimus_core_v1beta1_project_proto_init() } +func file_raystack_optimus_core_v1beta1_project_proto_init() { + if File_raystack_optimus_core_v1beta1_project_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_raystack_optimus_core_v1beta1_project_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterProjectRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_project_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterProjectResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_project_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListProjectsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_project_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListProjectsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_project_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetProjectRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_project_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetProjectResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_project_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProjectSpecification); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_project_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProjectSpecification_ProjectSecret); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_raystack_optimus_core_v1beta1_project_proto_rawDesc, + NumEnums: 0, + NumMessages: 9, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_raystack_optimus_core_v1beta1_project_proto_goTypes, + DependencyIndexes: file_raystack_optimus_core_v1beta1_project_proto_depIdxs, + MessageInfos: file_raystack_optimus_core_v1beta1_project_proto_msgTypes, + }.Build() + File_raystack_optimus_core_v1beta1_project_proto = out.File + file_raystack_optimus_core_v1beta1_project_proto_rawDesc = nil + file_raystack_optimus_core_v1beta1_project_proto_goTypes = nil + file_raystack_optimus_core_v1beta1_project_proto_depIdxs = nil +} diff --git a/protos/odpf/optimus/core/v1beta1/project.pb.gw.go b/protos/raystack/optimus/core/v1beta1/project.pb.gw.go similarity index 93% rename from protos/odpf/optimus/core/v1beta1/project.pb.gw.go rename to protos/raystack/optimus/core/v1beta1/project.pb.gw.go index 4c1b31be92..fe6409d0f3 100644 --- a/protos/odpf/optimus/core/v1beta1/project.pb.gw.go +++ b/protos/raystack/optimus/core/v1beta1/project.pb.gw.go @@ -1,5 +1,5 @@ // Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. -// source: odpf/optimus/core/v1beta1/project.proto +// source: raystack/optimus/core/v1beta1/project.proto /* Package optimus is a reverse proxy. @@ -147,7 +147,7 @@ func RegisterProjectServiceHandlerServer(ctx context.Context, mux *runtime.Serve var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.ProjectService/RegisterProject", runtime.WithHTTPPathPattern("/v1beta1/project")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.ProjectService/RegisterProject", runtime.WithHTTPPathPattern("/v1beta1/project")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -170,7 +170,7 @@ func RegisterProjectServiceHandlerServer(ctx context.Context, mux *runtime.Serve var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.ProjectService/ListProjects", runtime.WithHTTPPathPattern("/v1beta1/project")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.ProjectService/ListProjects", runtime.WithHTTPPathPattern("/v1beta1/project")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -193,7 +193,7 @@ func RegisterProjectServiceHandlerServer(ctx context.Context, mux *runtime.Serve var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.ProjectService/GetProject", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.ProjectService/GetProject", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -255,7 +255,7 @@ func RegisterProjectServiceHandlerClient(ctx context.Context, mux *runtime.Serve ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.ProjectService/RegisterProject", runtime.WithHTTPPathPattern("/v1beta1/project")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.ProjectService/RegisterProject", runtime.WithHTTPPathPattern("/v1beta1/project")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -275,7 +275,7 @@ func RegisterProjectServiceHandlerClient(ctx context.Context, mux *runtime.Serve ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.ProjectService/ListProjects", runtime.WithHTTPPathPattern("/v1beta1/project")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.ProjectService/ListProjects", runtime.WithHTTPPathPattern("/v1beta1/project")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -295,7 +295,7 @@ func RegisterProjectServiceHandlerClient(ctx context.Context, mux *runtime.Serve ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.ProjectService/GetProject", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.ProjectService/GetProject", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return diff --git a/protos/odpf/optimus/core/v1beta1/project.swagger.json b/protos/raystack/optimus/core/v1beta1/project.swagger.json similarity index 93% rename from protos/odpf/optimus/core/v1beta1/project.swagger.json rename to protos/raystack/optimus/core/v1beta1/project.swagger.json index 6911189870..1cb2d80429 100644 --- a/protos/odpf/optimus/core/v1beta1/project.swagger.json +++ b/protos/raystack/optimus/core/v1beta1/project.swagger.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "title": "odpf/optimus/core/v1beta1/project.proto", + "title": "raystack/optimus/core/v1beta1/project.proto", "version": "0.1" }, "tags": [ @@ -11,15 +11,9 @@ ], "host": "127.0.0.1:9100", "basePath": "/api", - "schemes": [ - "http" - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], + "schemes": ["http"], + "consumes": ["application/json"], + "produces": ["application/json"], "paths": { "/v1beta1/project": { "get": { @@ -39,9 +33,7 @@ } } }, - "tags": [ - "ProjectService" - ] + "tags": ["ProjectService"] }, "post": { "summary": "RegisterProject creates a new optimus project", @@ -70,9 +62,7 @@ } } ], - "tags": [ - "ProjectService" - ] + "tags": ["ProjectService"] } }, "/v1beta1/project/{projectName}": { @@ -101,9 +91,7 @@ "type": "string" } ], - "tags": [ - "ProjectService" - ] + "tags": ["ProjectService"] } } }, diff --git a/protos/odpf/optimus/core/v1beta1/project_grpc.pb.go b/protos/raystack/optimus/core/v1beta1/project_grpc.pb.go similarity index 90% rename from protos/odpf/optimus/core/v1beta1/project_grpc.pb.go rename to protos/raystack/optimus/core/v1beta1/project_grpc.pb.go index 0222f50557..b8b106f67e 100644 --- a/protos/odpf/optimus/core/v1beta1/project_grpc.pb.go +++ b/protos/raystack/optimus/core/v1beta1/project_grpc.pb.go @@ -2,7 +2,7 @@ // versions: // - protoc-gen-go-grpc v1.2.0 // - protoc (unknown) -// source: odpf/optimus/core/v1beta1/project.proto +// source: raystack/optimus/core/v1beta1/project.proto package optimus @@ -40,7 +40,7 @@ func NewProjectServiceClient(cc grpc.ClientConnInterface) ProjectServiceClient { func (c *projectServiceClient) RegisterProject(ctx context.Context, in *RegisterProjectRequest, opts ...grpc.CallOption) (*RegisterProjectResponse, error) { out := new(RegisterProjectResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.ProjectService/RegisterProject", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.ProjectService/RegisterProject", in, out, opts...) if err != nil { return nil, err } @@ -49,7 +49,7 @@ func (c *projectServiceClient) RegisterProject(ctx context.Context, in *Register func (c *projectServiceClient) ListProjects(ctx context.Context, in *ListProjectsRequest, opts ...grpc.CallOption) (*ListProjectsResponse, error) { out := new(ListProjectsResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.ProjectService/ListProjects", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.ProjectService/ListProjects", in, out, opts...) if err != nil { return nil, err } @@ -58,7 +58,7 @@ func (c *projectServiceClient) ListProjects(ctx context.Context, in *ListProject func (c *projectServiceClient) GetProject(ctx context.Context, in *GetProjectRequest, opts ...grpc.CallOption) (*GetProjectResponse, error) { out := new(GetProjectResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.ProjectService/GetProject", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.ProjectService/GetProject", in, out, opts...) if err != nil { return nil, err } @@ -114,7 +114,7 @@ func _ProjectService_RegisterProject_Handler(srv interface{}, ctx context.Contex } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.ProjectService/RegisterProject", + FullMethod: "/raystack.optimus.core.v1beta1.ProjectService/RegisterProject", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProjectServiceServer).RegisterProject(ctx, req.(*RegisterProjectRequest)) @@ -132,7 +132,7 @@ func _ProjectService_ListProjects_Handler(srv interface{}, ctx context.Context, } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.ProjectService/ListProjects", + FullMethod: "/raystack.optimus.core.v1beta1.ProjectService/ListProjects", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProjectServiceServer).ListProjects(ctx, req.(*ListProjectsRequest)) @@ -150,7 +150,7 @@ func _ProjectService_GetProject_Handler(srv interface{}, ctx context.Context, de } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.ProjectService/GetProject", + FullMethod: "/raystack.optimus.core.v1beta1.ProjectService/GetProject", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ProjectServiceServer).GetProject(ctx, req.(*GetProjectRequest)) @@ -162,7 +162,7 @@ func _ProjectService_GetProject_Handler(srv interface{}, ctx context.Context, de // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var ProjectService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "odpf.optimus.core.v1beta1.ProjectService", + ServiceName: "raystack.optimus.core.v1beta1.ProjectService", HandlerType: (*ProjectServiceServer)(nil), Methods: []grpc.MethodDesc{ { @@ -179,5 +179,5 @@ var ProjectService_ServiceDesc = grpc.ServiceDesc{ }, }, Streams: []grpc.StreamDesc{}, - Metadata: "odpf/optimus/core/v1beta1/project.proto", + Metadata: "raystack/optimus/core/v1beta1/project.proto", } diff --git a/protos/raystack/optimus/core/v1beta1/replay.pb.go b/protos/raystack/optimus/core/v1beta1/replay.pb.go new file mode 100644 index 0000000000..727f68fa3d --- /dev/null +++ b/protos/raystack/optimus/core/v1beta1/replay.pb.go @@ -0,0 +1,1069 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.0 +// protoc (unknown) +// source: raystack/optimus/core/v1beta1/replay.proto + +package optimus + +import ( + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ListReplayRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` +} + +func (x *ListReplayRequest) Reset() { + *x = ListReplayRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListReplayRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListReplayRequest) ProtoMessage() {} + +func (x *ListReplayRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListReplayRequest.ProtoReflect.Descriptor instead. +func (*ListReplayRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_replay_proto_rawDescGZIP(), []int{0} +} + +func (x *ListReplayRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +type ListReplayResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Replays []*GetReplayResponse `protobuf:"bytes,1,rep,name=replays,proto3" json:"replays,omitempty"` +} + +func (x *ListReplayResponse) Reset() { + *x = ListReplayResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListReplayResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListReplayResponse) ProtoMessage() {} + +func (x *ListReplayResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListReplayResponse.ProtoReflect.Descriptor instead. +func (*ListReplayResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_replay_proto_rawDescGZIP(), []int{1} +} + +func (x *ListReplayResponse) GetReplays() []*GetReplayResponse { + if x != nil { + return x.Replays + } + return nil +} + +type GetReplayRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ReplayId string `protobuf:"bytes,1,opt,name=replay_id,json=replayId,proto3" json:"replay_id,omitempty"` + ProjectName string `protobuf:"bytes,2,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` +} + +func (x *GetReplayRequest) Reset() { + *x = GetReplayRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetReplayRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetReplayRequest) ProtoMessage() {} + +func (x *GetReplayRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetReplayRequest.ProtoReflect.Descriptor instead. +func (*GetReplayRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_replay_proto_rawDescGZIP(), []int{2} +} + +func (x *GetReplayRequest) GetReplayId() string { + if x != nil { + return x.ReplayId + } + return "" +} + +func (x *GetReplayRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +type GetReplayResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + JobName string `protobuf:"bytes,2,opt,name=job_name,json=jobName,proto3" json:"job_name,omitempty"` + Status string `protobuf:"bytes,3,opt,name=status,proto3" json:"status,omitempty"` + ReplayConfig *ReplayConfig `protobuf:"bytes,4,opt,name=replay_config,json=replayConfig,proto3" json:"replay_config,omitempty"` + ReplayRuns []*ReplayRun `protobuf:"bytes,5,rep,name=replay_runs,json=replayRuns,proto3" json:"replay_runs,omitempty"` +} + +func (x *GetReplayResponse) Reset() { + *x = GetReplayResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetReplayResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetReplayResponse) ProtoMessage() {} + +func (x *GetReplayResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetReplayResponse.ProtoReflect.Descriptor instead. +func (*GetReplayResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_replay_proto_rawDescGZIP(), []int{3} +} + +func (x *GetReplayResponse) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *GetReplayResponse) GetJobName() string { + if x != nil { + return x.JobName + } + return "" +} + +func (x *GetReplayResponse) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *GetReplayResponse) GetReplayConfig() *ReplayConfig { + if x != nil { + return x.ReplayConfig + } + return nil +} + +func (x *GetReplayResponse) GetReplayRuns() []*ReplayRun { + if x != nil { + return x.ReplayRuns + } + return nil +} + +type ReplayConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + StartTime *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` + Parallel bool `protobuf:"varint,3,opt,name=parallel,proto3" json:"parallel,omitempty"` + JobConfig map[string]string `protobuf:"bytes,4,rep,name=job_config,json=jobConfig,proto3" json:"job_config,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty"` +} + +func (x *ReplayConfig) Reset() { + *x = ReplayConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReplayConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReplayConfig) ProtoMessage() {} + +func (x *ReplayConfig) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReplayConfig.ProtoReflect.Descriptor instead. +func (*ReplayConfig) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_replay_proto_rawDescGZIP(), []int{4} +} + +func (x *ReplayConfig) GetStartTime() *timestamppb.Timestamp { + if x != nil { + return x.StartTime + } + return nil +} + +func (x *ReplayConfig) GetEndTime() *timestamppb.Timestamp { + if x != nil { + return x.EndTime + } + return nil +} + +func (x *ReplayConfig) GetParallel() bool { + if x != nil { + return x.Parallel + } + return false +} + +func (x *ReplayConfig) GetJobConfig() map[string]string { + if x != nil { + return x.JobConfig + } + return nil +} + +func (x *ReplayConfig) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +type ReplayRun struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ScheduledAt *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=scheduled_at,json=scheduledAt,proto3" json:"scheduled_at,omitempty"` + Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` +} + +func (x *ReplayRun) Reset() { + *x = ReplayRun{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReplayRun) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReplayRun) ProtoMessage() {} + +func (x *ReplayRun) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReplayRun.ProtoReflect.Descriptor instead. +func (*ReplayRun) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_replay_proto_rawDescGZIP(), []int{5} +} + +func (x *ReplayRun) GetScheduledAt() *timestamppb.Timestamp { + if x != nil { + return x.ScheduledAt + } + return nil +} + +func (x *ReplayRun) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +type ReplayDryRunResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ReplayRuns []*ReplayRun `protobuf:"bytes,1,rep,name=replay_runs,json=replayRuns,proto3" json:"replay_runs,omitempty"` +} + +func (x *ReplayDryRunResponse) Reset() { + *x = ReplayDryRunResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReplayDryRunResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReplayDryRunResponse) ProtoMessage() {} + +func (x *ReplayDryRunResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReplayDryRunResponse.ProtoReflect.Descriptor instead. +func (*ReplayDryRunResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_replay_proto_rawDescGZIP(), []int{6} +} + +func (x *ReplayDryRunResponse) GetReplayRuns() []*ReplayRun { + if x != nil { + return x.ReplayRuns + } + return nil +} + +type ReplayRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + JobName string `protobuf:"bytes,2,opt,name=job_name,json=jobName,proto3" json:"job_name,omitempty"` + NamespaceName string `protobuf:"bytes,3,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` + StartTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` + Parallel bool `protobuf:"varint,6,opt,name=parallel,proto3" json:"parallel,omitempty"` + Description string `protobuf:"bytes,7,opt,name=description,proto3" json:"description,omitempty"` + JobConfig string `protobuf:"bytes,8,opt,name=job_config,json=jobConfig,proto3" json:"job_config,omitempty"` +} + +func (x *ReplayRequest) Reset() { + *x = ReplayRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReplayRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReplayRequest) ProtoMessage() {} + +func (x *ReplayRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReplayRequest.ProtoReflect.Descriptor instead. +func (*ReplayRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_replay_proto_rawDescGZIP(), []int{7} +} + +func (x *ReplayRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *ReplayRequest) GetJobName() string { + if x != nil { + return x.JobName + } + return "" +} + +func (x *ReplayRequest) GetNamespaceName() string { + if x != nil { + return x.NamespaceName + } + return "" +} + +func (x *ReplayRequest) GetStartTime() *timestamppb.Timestamp { + if x != nil { + return x.StartTime + } + return nil +} + +func (x *ReplayRequest) GetEndTime() *timestamppb.Timestamp { + if x != nil { + return x.EndTime + } + return nil +} + +func (x *ReplayRequest) GetParallel() bool { + if x != nil { + return x.Parallel + } + return false +} + +func (x *ReplayRequest) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *ReplayRequest) GetJobConfig() string { + if x != nil { + return x.JobConfig + } + return "" +} + +type ReplayDryRunRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + JobName string `protobuf:"bytes,2,opt,name=job_name,json=jobName,proto3" json:"job_name,omitempty"` + NamespaceName string `protobuf:"bytes,3,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` + StartTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` + Parallel bool `protobuf:"varint,6,opt,name=parallel,proto3" json:"parallel,omitempty"` + Description string `protobuf:"bytes,7,opt,name=description,proto3" json:"description,omitempty"` + JobConfig string `protobuf:"bytes,8,opt,name=job_config,json=jobConfig,proto3" json:"job_config,omitempty"` +} + +func (x *ReplayDryRunRequest) Reset() { + *x = ReplayDryRunRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReplayDryRunRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReplayDryRunRequest) ProtoMessage() {} + +func (x *ReplayDryRunRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReplayDryRunRequest.ProtoReflect.Descriptor instead. +func (*ReplayDryRunRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_replay_proto_rawDescGZIP(), []int{8} +} + +func (x *ReplayDryRunRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *ReplayDryRunRequest) GetJobName() string { + if x != nil { + return x.JobName + } + return "" +} + +func (x *ReplayDryRunRequest) GetNamespaceName() string { + if x != nil { + return x.NamespaceName + } + return "" +} + +func (x *ReplayDryRunRequest) GetStartTime() *timestamppb.Timestamp { + if x != nil { + return x.StartTime + } + return nil +} + +func (x *ReplayDryRunRequest) GetEndTime() *timestamppb.Timestamp { + if x != nil { + return x.EndTime + } + return nil +} + +func (x *ReplayDryRunRequest) GetParallel() bool { + if x != nil { + return x.Parallel + } + return false +} + +func (x *ReplayDryRunRequest) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *ReplayDryRunRequest) GetJobConfig() string { + if x != nil { + return x.JobConfig + } + return "" +} + +type ReplayResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *ReplayResponse) Reset() { + *x = ReplayResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReplayResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReplayResponse) ProtoMessage() {} + +func (x *ReplayResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReplayResponse.ProtoReflect.Descriptor instead. +func (*ReplayResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_replay_proto_rawDescGZIP(), []int{9} +} + +func (x *ReplayResponse) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +var File_raystack_optimus_core_v1beta1_replay_proto protoreflect.FileDescriptor + +var file_raystack_optimus_core_v1beta1_replay_proto_rawDesc = []byte{ + 0x0a, 0x2d, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2f, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x20, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, + 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, + 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, + 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x22, 0x36, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x63, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, + 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, + 0x0a, 0x07, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x33, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x07, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x73, 0x22, 0x52, 0x0a, + 0x10, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x49, 0x64, 0x12, 0x21, + 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, + 0x65, 0x22, 0xf9, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x53, 0x0a, 0x0d, 0x72, 0x65, + 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2e, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, + 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x52, 0x0c, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x4c, 0x0a, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x73, 0x18, 0x05, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x75, + 0x6e, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x75, 0x6e, 0x73, 0x22, 0xda, 0x02, + 0x0a, 0x0c, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x39, + 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x65, 0x6e, 0x64, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, + 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x08, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x12, 0x5c, 0x0a, 0x0a, + 0x6a, 0x6f, 0x62, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x3d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, + 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x2e, 0x4a, 0x6f, 0x62, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x09, 0x6a, 0x6f, 0x62, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x3c, 0x0a, 0x0e, + 0x4a, 0x6f, 0x62, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x62, 0x0a, 0x09, 0x52, 0x65, + 0x70, 0x6c, 0x61, 0x79, 0x52, 0x75, 0x6e, 0x12, 0x3d, 0x0a, 0x0c, 0x73, 0x63, 0x68, 0x65, 0x64, + 0x75, 0x6c, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x73, 0x63, 0x68, 0x65, 0x64, + 0x75, 0x6c, 0x65, 0x64, 0x41, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x64, + 0x0a, 0x14, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, + 0x5f, 0x72, 0x75, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x67, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, + 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, + 0x65, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x75, 0x6e, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, + 0x52, 0x75, 0x6e, 0x73, 0x22, 0xc3, 0x02, 0x0a, 0x0d, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1a, 0x0a, + 0x08, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x08, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x6a, + 0x6f, 0x62, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x6a, 0x6f, 0x62, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xc9, 0x02, 0x0a, 0x13, 0x52, + 0x65, 0x70, 0x6c, 0x61, 0x79, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, + 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x72, + 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x70, 0x61, 0x72, + 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x6a, 0x6f, 0x62, 0x5f, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6a, 0x6f, 0x62, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x20, 0x0a, 0x0e, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x32, 0xc8, 0x05, 0x0a, 0x0d, 0x52, 0x65, 0x70, + 0x6c, 0x61, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x9e, 0x01, 0x0a, 0x06, 0x52, + 0x65, 0x70, 0x6c, 0x61, 0x79, 0x12, 0x2f, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, + 0x22, 0x26, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x7d, 0x2f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x01, 0x2a, 0x12, 0xb8, 0x01, 0x0a, 0x0c, + 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, 0x35, 0x2e, 0x67, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, + 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x44, 0x72, 0x79, + 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x39, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x33, 0x22, 0x2e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x2d, 0x64, 0x72, 0x79, 0x2d, + 0x72, 0x75, 0x6e, 0x3a, 0x01, 0x2a, 0x12, 0xa7, 0x01, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x65, 0x70, 0x6c, 0x61, 0x79, 0x12, 0x33, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, + 0x6c, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x67, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, + 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x12, 0x26, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, + 0x12, 0xb0, 0x01, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x12, 0x32, + 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x34, 0x12, + 0x32, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, + 0x2f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x2f, 0x7b, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x5f, + 0x69, 0x64, 0x7d, 0x42, 0x95, 0x01, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2e, 0x6f, + 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x42, 0x14, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x1e, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x74, 0x6f, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x92, 0x41, + 0x3a, 0x12, 0x05, 0x32, 0x03, 0x30, 0x2e, 0x31, 0x1a, 0x0e, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, + 0x30, 0x2e, 0x31, 0x3a, 0x39, 0x31, 0x30, 0x30, 0x22, 0x04, 0x2f, 0x61, 0x70, 0x69, 0x2a, 0x01, + 0x01, 0x72, 0x18, 0x0a, 0x16, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x20, 0x52, 0x65, 0x70, + 0x6c, 0x61, 0x79, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +} + +var ( + file_raystack_optimus_core_v1beta1_replay_proto_rawDescOnce sync.Once + file_raystack_optimus_core_v1beta1_replay_proto_rawDescData = file_raystack_optimus_core_v1beta1_replay_proto_rawDesc +) + +func file_raystack_optimus_core_v1beta1_replay_proto_rawDescGZIP() []byte { + file_raystack_optimus_core_v1beta1_replay_proto_rawDescOnce.Do(func() { + file_raystack_optimus_core_v1beta1_replay_proto_rawDescData = protoimpl.X.CompressGZIP(file_raystack_optimus_core_v1beta1_replay_proto_rawDescData) + }) + return file_raystack_optimus_core_v1beta1_replay_proto_rawDescData +} + +var file_raystack_optimus_core_v1beta1_replay_proto_msgTypes = make([]protoimpl.MessageInfo, 11) +var file_raystack_optimus_core_v1beta1_replay_proto_goTypes = []interface{}{ + (*ListReplayRequest)(nil), // 0: raystack.optimus.core.v1beta1.ListReplayRequest + (*ListReplayResponse)(nil), // 1: raystack.optimus.core.v1beta1.ListReplayResponse + (*GetReplayRequest)(nil), // 2: raystack.optimus.core.v1beta1.GetReplayRequest + (*GetReplayResponse)(nil), // 3: raystack.optimus.core.v1beta1.GetReplayResponse + (*ReplayConfig)(nil), // 4: raystack.optimus.core.v1beta1.ReplayConfig + (*ReplayRun)(nil), // 5: raystack.optimus.core.v1beta1.ReplayRun + (*ReplayDryRunResponse)(nil), // 6: raystack.optimus.core.v1beta1.ReplayDryRunResponse + (*ReplayRequest)(nil), // 7: raystack.optimus.core.v1beta1.ReplayRequest + (*ReplayDryRunRequest)(nil), // 8: raystack.optimus.core.v1beta1.ReplayDryRunRequest + (*ReplayResponse)(nil), // 9: raystack.optimus.core.v1beta1.ReplayResponse + nil, // 10: raystack.optimus.core.v1beta1.ReplayConfig.JobConfigEntry + (*timestamppb.Timestamp)(nil), // 11: google.protobuf.Timestamp +} +var file_raystack_optimus_core_v1beta1_replay_proto_depIdxs = []int32{ + 3, // 0: raystack.optimus.core.v1beta1.ListReplayResponse.replays:type_name -> raystack.optimus.core.v1beta1.GetReplayResponse + 4, // 1: raystack.optimus.core.v1beta1.GetReplayResponse.replay_config:type_name -> raystack.optimus.core.v1beta1.ReplayConfig + 5, // 2: raystack.optimus.core.v1beta1.GetReplayResponse.replay_runs:type_name -> raystack.optimus.core.v1beta1.ReplayRun + 11, // 3: raystack.optimus.core.v1beta1.ReplayConfig.start_time:type_name -> google.protobuf.Timestamp + 11, // 4: raystack.optimus.core.v1beta1.ReplayConfig.end_time:type_name -> google.protobuf.Timestamp + 10, // 5: raystack.optimus.core.v1beta1.ReplayConfig.job_config:type_name -> raystack.optimus.core.v1beta1.ReplayConfig.JobConfigEntry + 11, // 6: raystack.optimus.core.v1beta1.ReplayRun.scheduled_at:type_name -> google.protobuf.Timestamp + 5, // 7: raystack.optimus.core.v1beta1.ReplayDryRunResponse.replay_runs:type_name -> raystack.optimus.core.v1beta1.ReplayRun + 11, // 8: raystack.optimus.core.v1beta1.ReplayRequest.start_time:type_name -> google.protobuf.Timestamp + 11, // 9: raystack.optimus.core.v1beta1.ReplayRequest.end_time:type_name -> google.protobuf.Timestamp + 11, // 10: raystack.optimus.core.v1beta1.ReplayDryRunRequest.start_time:type_name -> google.protobuf.Timestamp + 11, // 11: raystack.optimus.core.v1beta1.ReplayDryRunRequest.end_time:type_name -> google.protobuf.Timestamp + 7, // 12: raystack.optimus.core.v1beta1.ReplayService.Replay:input_type -> raystack.optimus.core.v1beta1.ReplayRequest + 8, // 13: raystack.optimus.core.v1beta1.ReplayService.ReplayDryRun:input_type -> raystack.optimus.core.v1beta1.ReplayDryRunRequest + 0, // 14: raystack.optimus.core.v1beta1.ReplayService.ListReplay:input_type -> raystack.optimus.core.v1beta1.ListReplayRequest + 2, // 15: raystack.optimus.core.v1beta1.ReplayService.GetReplay:input_type -> raystack.optimus.core.v1beta1.GetReplayRequest + 9, // 16: raystack.optimus.core.v1beta1.ReplayService.Replay:output_type -> raystack.optimus.core.v1beta1.ReplayResponse + 6, // 17: raystack.optimus.core.v1beta1.ReplayService.ReplayDryRun:output_type -> raystack.optimus.core.v1beta1.ReplayDryRunResponse + 1, // 18: raystack.optimus.core.v1beta1.ReplayService.ListReplay:output_type -> raystack.optimus.core.v1beta1.ListReplayResponse + 3, // 19: raystack.optimus.core.v1beta1.ReplayService.GetReplay:output_type -> raystack.optimus.core.v1beta1.GetReplayResponse + 16, // [16:20] is the sub-list for method output_type + 12, // [12:16] is the sub-list for method input_type + 12, // [12:12] is the sub-list for extension type_name + 12, // [12:12] is the sub-list for extension extendee + 0, // [0:12] is the sub-list for field type_name +} + +func init() { file_raystack_optimus_core_v1beta1_replay_proto_init() } +func file_raystack_optimus_core_v1beta1_replay_proto_init() { + if File_raystack_optimus_core_v1beta1_replay_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListReplayRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListReplayResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetReplayRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetReplayResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReplayConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReplayRun); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReplayDryRunResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReplayRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReplayDryRunRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_replay_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReplayResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_raystack_optimus_core_v1beta1_replay_proto_rawDesc, + NumEnums: 0, + NumMessages: 11, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_raystack_optimus_core_v1beta1_replay_proto_goTypes, + DependencyIndexes: file_raystack_optimus_core_v1beta1_replay_proto_depIdxs, + MessageInfos: file_raystack_optimus_core_v1beta1_replay_proto_msgTypes, + }.Build() + File_raystack_optimus_core_v1beta1_replay_proto = out.File + file_raystack_optimus_core_v1beta1_replay_proto_rawDesc = nil + file_raystack_optimus_core_v1beta1_replay_proto_goTypes = nil + file_raystack_optimus_core_v1beta1_replay_proto_depIdxs = nil +} diff --git a/protos/raystack/optimus/core/v1beta1/replay.pb.gw.go b/protos/raystack/optimus/core/v1beta1/replay.pb.gw.go new file mode 100644 index 0000000000..332816ce47 --- /dev/null +++ b/protos/raystack/optimus/core/v1beta1/replay.pb.gw.go @@ -0,0 +1,534 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: raystack/optimus/core/v1beta1/replay.proto + +/* +Package optimus is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package optimus + +import ( + "context" + "io" + "net/http" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = metadata.Join + +func request_ReplayService_Replay_0(ctx context.Context, marshaler runtime.Marshaler, client ReplayServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ReplayRequest + 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); 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["project_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "project_name") + } + + protoReq.ProjectName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "project_name", err) + } + + msg, err := client.Replay(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_ReplayService_Replay_0(ctx context.Context, marshaler runtime.Marshaler, server ReplayServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ReplayRequest + 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); 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["project_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "project_name") + } + + protoReq.ProjectName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "project_name", err) + } + + msg, err := server.Replay(ctx, &protoReq) + return msg, metadata, err + +} + +func request_ReplayService_ReplayDryRun_0(ctx context.Context, marshaler runtime.Marshaler, client ReplayServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ReplayDryRunRequest + 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); 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["project_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "project_name") + } + + protoReq.ProjectName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "project_name", err) + } + + msg, err := client.ReplayDryRun(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_ReplayService_ReplayDryRun_0(ctx context.Context, marshaler runtime.Marshaler, server ReplayServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ReplayDryRunRequest + 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); 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["project_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "project_name") + } + + protoReq.ProjectName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "project_name", err) + } + + msg, err := server.ReplayDryRun(ctx, &protoReq) + return msg, metadata, err + +} + +func request_ReplayService_ListReplay_0(ctx context.Context, marshaler runtime.Marshaler, client ReplayServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListReplayRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["project_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "project_name") + } + + protoReq.ProjectName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "project_name", err) + } + + msg, err := client.ListReplay(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_ReplayService_ListReplay_0(ctx context.Context, marshaler runtime.Marshaler, server ReplayServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListReplayRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["project_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "project_name") + } + + protoReq.ProjectName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "project_name", err) + } + + msg, err := server.ListReplay(ctx, &protoReq) + return msg, metadata, err + +} + +func request_ReplayService_GetReplay_0(ctx context.Context, marshaler runtime.Marshaler, client ReplayServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetReplayRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["project_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "project_name") + } + + protoReq.ProjectName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "project_name", err) + } + + val, ok = pathParams["replay_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "replay_id") + } + + protoReq.ReplayId, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "replay_id", err) + } + + msg, err := client.GetReplay(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_ReplayService_GetReplay_0(ctx context.Context, marshaler runtime.Marshaler, server ReplayServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetReplayRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["project_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "project_name") + } + + protoReq.ProjectName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "project_name", err) + } + + val, ok = pathParams["replay_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "replay_id") + } + + protoReq.ReplayId, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "replay_id", err) + } + + msg, err := server.GetReplay(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterReplayServiceHandlerServer registers the http handlers for service ReplayService to "mux". +// UnaryRPC :call ReplayServiceServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterReplayServiceHandlerFromEndpoint instead. +func RegisterReplayServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server ReplayServiceServer) error { + + mux.Handle("POST", pattern_ReplayService_Replay_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.ReplayService/Replay", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/replay")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_ReplayService_Replay_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_ReplayService_Replay_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_ReplayService_ReplayDryRun_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.ReplayService/ReplayDryRun", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/replay-dry-run")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_ReplayService_ReplayDryRun_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_ReplayService_ReplayDryRun_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_ReplayService_ListReplay_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.ReplayService/ListReplay", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/replay")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_ReplayService_ListReplay_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_ReplayService_ListReplay_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_ReplayService_GetReplay_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.ReplayService/GetReplay", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/replay/{replay_id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_ReplayService_GetReplay_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_ReplayService_GetReplay_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterReplayServiceHandlerFromEndpoint is same as RegisterReplayServiceHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterReplayServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterReplayServiceHandler(ctx, mux, conn) +} + +// RegisterReplayServiceHandler registers the http handlers for service ReplayService to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterReplayServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterReplayServiceHandlerClient(ctx, mux, NewReplayServiceClient(conn)) +} + +// RegisterReplayServiceHandlerClient registers the http handlers for service ReplayService +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "ReplayServiceClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "ReplayServiceClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "ReplayServiceClient" to call the correct interceptors. +func RegisterReplayServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client ReplayServiceClient) error { + + mux.Handle("POST", pattern_ReplayService_Replay_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, "/raystack.optimus.core.v1beta1.ReplayService/Replay", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/replay")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_ReplayService_Replay_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_ReplayService_Replay_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_ReplayService_ReplayDryRun_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, "/raystack.optimus.core.v1beta1.ReplayService/ReplayDryRun", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/replay-dry-run")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_ReplayService_ReplayDryRun_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_ReplayService_ReplayDryRun_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_ReplayService_ListReplay_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, "/raystack.optimus.core.v1beta1.ReplayService/ListReplay", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/replay")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_ReplayService_ListReplay_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_ReplayService_ListReplay_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_ReplayService_GetReplay_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, "/raystack.optimus.core.v1beta1.ReplayService/GetReplay", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/replay/{replay_id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_ReplayService_GetReplay_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_ReplayService_GetReplay_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_ReplayService_Replay_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"v1beta1", "project", "project_name", "replay"}, "")) + + pattern_ReplayService_ReplayDryRun_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"v1beta1", "project", "project_name", "replay-dry-run"}, "")) + + pattern_ReplayService_ListReplay_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"v1beta1", "project", "project_name", "replay"}, "")) + + pattern_ReplayService_GetReplay_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{"v1beta1", "project", "project_name", "replay", "replay_id"}, "")) +) + +var ( + forward_ReplayService_Replay_0 = runtime.ForwardResponseMessage + + forward_ReplayService_ReplayDryRun_0 = runtime.ForwardResponseMessage + + forward_ReplayService_ListReplay_0 = runtime.ForwardResponseMessage + + forward_ReplayService_GetReplay_0 = runtime.ForwardResponseMessage +) diff --git a/protos/raystack/optimus/core/v1beta1/replay.swagger.json b/protos/raystack/optimus/core/v1beta1/replay.swagger.json new file mode 100644 index 0000000000..3f8c70f04b --- /dev/null +++ b/protos/raystack/optimus/core/v1beta1/replay.swagger.json @@ -0,0 +1,326 @@ +{ + "swagger": "2.0", + "info": { + "title": "raystack/optimus/core/v1beta1/replay.proto", + "version": "0.1" + }, + "tags": [ + { + "name": "ReplayService" + } + ], + "host": "127.0.0.1:9100", + "basePath": "/api", + "schemes": ["http"], + "consumes": ["application/json"], + "produces": ["application/json"], + "paths": { + "/v1beta1/project/{projectName}/replay": { + "get": { + "operationId": "ReplayService_ListReplay", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1beta1ListReplayResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "projectName", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": ["ReplayService"] + }, + "post": { + "operationId": "ReplayService_Replay", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1beta1ReplayResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "projectName", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "jobName": { + "type": "string" + }, + "namespaceName": { + "type": "string" + }, + "startTime": { + "type": "string", + "format": "date-time" + }, + "endTime": { + "type": "string", + "format": "date-time" + }, + "parallel": { + "type": "boolean" + }, + "description": { + "type": "string" + }, + "jobConfig": { + "type": "string" + } + } + } + } + ], + "tags": ["ReplayService"] + } + }, + "/v1beta1/project/{projectName}/replay-dry-run": { + "post": { + "operationId": "ReplayService_ReplayDryRun", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1beta1ReplayDryRunResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "projectName", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "jobName": { + "type": "string" + }, + "namespaceName": { + "type": "string" + }, + "startTime": { + "type": "string", + "format": "date-time" + }, + "endTime": { + "type": "string", + "format": "date-time" + }, + "parallel": { + "type": "boolean" + }, + "description": { + "type": "string" + }, + "jobConfig": { + "type": "string" + } + } + } + } + ], + "tags": ["ReplayService"] + } + }, + "/v1beta1/project/{projectName}/replay/{replayId}": { + "get": { + "operationId": "ReplayService_GetReplay", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1beta1GetReplayResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "projectName", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "replayId", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": ["ReplayService"] + } + } + }, + "definitions": { + "protobufAny": { + "type": "object", + "properties": { + "typeUrl": { + "type": "string" + }, + "value": { + "type": "string", + "format": "byte" + } + } + }, + "rpcStatus": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "details": { + "type": "array", + "items": { + "$ref": "#/definitions/protobufAny" + } + } + } + }, + "v1beta1GetReplayResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "jobName": { + "type": "string" + }, + "status": { + "type": "string" + }, + "replayConfig": { + "$ref": "#/definitions/v1beta1ReplayConfig" + }, + "replayRuns": { + "type": "array", + "items": { + "$ref": "#/definitions/v1beta1ReplayRun" + } + } + } + }, + "v1beta1ListReplayResponse": { + "type": "object", + "properties": { + "replays": { + "type": "array", + "items": { + "$ref": "#/definitions/v1beta1GetReplayResponse" + } + } + } + }, + "v1beta1ReplayConfig": { + "type": "object", + "properties": { + "startTime": { + "type": "string", + "format": "date-time" + }, + "endTime": { + "type": "string", + "format": "date-time" + }, + "parallel": { + "type": "boolean" + }, + "jobConfig": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "description": { + "type": "string" + } + } + }, + "v1beta1ReplayDryRunResponse": { + "type": "object", + "properties": { + "replayRuns": { + "type": "array", + "items": { + "$ref": "#/definitions/v1beta1ReplayRun" + } + } + } + }, + "v1beta1ReplayResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "v1beta1ReplayRun": { + "type": "object", + "properties": { + "scheduledAt": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string" + } + } + } + }, + "externalDocs": { + "description": "Optimus Replay Service" + } +} diff --git a/protos/raystack/optimus/core/v1beta1/replay_grpc.pb.go b/protos/raystack/optimus/core/v1beta1/replay_grpc.pb.go new file mode 100644 index 0000000000..a6712ae40a --- /dev/null +++ b/protos/raystack/optimus/core/v1beta1/replay_grpc.pb.go @@ -0,0 +1,213 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.2.0 +// - protoc (unknown) +// source: raystack/optimus/core/v1beta1/replay.proto + +package optimus + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// ReplayServiceClient is the client API for ReplayService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type ReplayServiceClient interface { + Replay(ctx context.Context, in *ReplayRequest, opts ...grpc.CallOption) (*ReplayResponse, error) + ReplayDryRun(ctx context.Context, in *ReplayDryRunRequest, opts ...grpc.CallOption) (*ReplayDryRunResponse, error) + ListReplay(ctx context.Context, in *ListReplayRequest, opts ...grpc.CallOption) (*ListReplayResponse, error) + GetReplay(ctx context.Context, in *GetReplayRequest, opts ...grpc.CallOption) (*GetReplayResponse, error) +} + +type replayServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewReplayServiceClient(cc grpc.ClientConnInterface) ReplayServiceClient { + return &replayServiceClient{cc} +} + +func (c *replayServiceClient) Replay(ctx context.Context, in *ReplayRequest, opts ...grpc.CallOption) (*ReplayResponse, error) { + out := new(ReplayResponse) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.ReplayService/Replay", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *replayServiceClient) ReplayDryRun(ctx context.Context, in *ReplayDryRunRequest, opts ...grpc.CallOption) (*ReplayDryRunResponse, error) { + out := new(ReplayDryRunResponse) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.ReplayService/ReplayDryRun", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *replayServiceClient) ListReplay(ctx context.Context, in *ListReplayRequest, opts ...grpc.CallOption) (*ListReplayResponse, error) { + out := new(ListReplayResponse) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.ReplayService/ListReplay", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *replayServiceClient) GetReplay(ctx context.Context, in *GetReplayRequest, opts ...grpc.CallOption) (*GetReplayResponse, error) { + out := new(GetReplayResponse) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.ReplayService/GetReplay", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ReplayServiceServer is the server API for ReplayService service. +// All implementations must embed UnimplementedReplayServiceServer +// for forward compatibility +type ReplayServiceServer interface { + Replay(context.Context, *ReplayRequest) (*ReplayResponse, error) + ReplayDryRun(context.Context, *ReplayDryRunRequest) (*ReplayDryRunResponse, error) + ListReplay(context.Context, *ListReplayRequest) (*ListReplayResponse, error) + GetReplay(context.Context, *GetReplayRequest) (*GetReplayResponse, error) + mustEmbedUnimplementedReplayServiceServer() +} + +// UnimplementedReplayServiceServer must be embedded to have forward compatible implementations. +type UnimplementedReplayServiceServer struct { +} + +func (UnimplementedReplayServiceServer) Replay(context.Context, *ReplayRequest) (*ReplayResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Replay not implemented") +} +func (UnimplementedReplayServiceServer) ReplayDryRun(context.Context, *ReplayDryRunRequest) (*ReplayDryRunResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ReplayDryRun not implemented") +} +func (UnimplementedReplayServiceServer) ListReplay(context.Context, *ListReplayRequest) (*ListReplayResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListReplay not implemented") +} +func (UnimplementedReplayServiceServer) GetReplay(context.Context, *GetReplayRequest) (*GetReplayResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetReplay not implemented") +} +func (UnimplementedReplayServiceServer) mustEmbedUnimplementedReplayServiceServer() {} + +// UnsafeReplayServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to ReplayServiceServer will +// result in compilation errors. +type UnsafeReplayServiceServer interface { + mustEmbedUnimplementedReplayServiceServer() +} + +func RegisterReplayServiceServer(s grpc.ServiceRegistrar, srv ReplayServiceServer) { + s.RegisterService(&ReplayService_ServiceDesc, srv) +} + +func _ReplayService_Replay_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ReplayRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReplayServiceServer).Replay(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/raystack.optimus.core.v1beta1.ReplayService/Replay", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReplayServiceServer).Replay(ctx, req.(*ReplayRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ReplayService_ReplayDryRun_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ReplayDryRunRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReplayServiceServer).ReplayDryRun(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/raystack.optimus.core.v1beta1.ReplayService/ReplayDryRun", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReplayServiceServer).ReplayDryRun(ctx, req.(*ReplayDryRunRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ReplayService_ListReplay_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListReplayRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReplayServiceServer).ListReplay(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/raystack.optimus.core.v1beta1.ReplayService/ListReplay", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReplayServiceServer).ListReplay(ctx, req.(*ListReplayRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ReplayService_GetReplay_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetReplayRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReplayServiceServer).GetReplay(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/raystack.optimus.core.v1beta1.ReplayService/GetReplay", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReplayServiceServer).GetReplay(ctx, req.(*GetReplayRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// ReplayService_ServiceDesc is the grpc.ServiceDesc for ReplayService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var ReplayService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "raystack.optimus.core.v1beta1.ReplayService", + HandlerType: (*ReplayServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Replay", + Handler: _ReplayService_Replay_Handler, + }, + { + MethodName: "ReplayDryRun", + Handler: _ReplayService_ReplayDryRun_Handler, + }, + { + MethodName: "ListReplay", + Handler: _ReplayService_ListReplay_Handler, + }, + { + MethodName: "GetReplay", + Handler: _ReplayService_GetReplay_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "raystack/optimus/core/v1beta1/replay.proto", +} diff --git a/protos/raystack/optimus/core/v1beta1/resource.pb.go b/protos/raystack/optimus/core/v1beta1/resource.pb.go new file mode 100644 index 0000000000..0cd90a6b12 --- /dev/null +++ b/protos/raystack/optimus/core/v1beta1/resource.pb.go @@ -0,0 +1,1616 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.0 +// protoc (unknown) +// source: raystack/optimus/core/v1beta1/resource.proto + +package optimus + +import ( + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + structpb "google.golang.org/protobuf/types/known/structpb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type DeployResourceSpecificationRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + DatastoreName string `protobuf:"bytes,2,opt,name=datastore_name,json=datastoreName,proto3" json:"datastore_name,omitempty"` + Resources []*ResourceSpecification `protobuf:"bytes,3,rep,name=resources,proto3" json:"resources,omitempty"` + NamespaceName string `protobuf:"bytes,4,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` +} + +func (x *DeployResourceSpecificationRequest) Reset() { + *x = DeployResourceSpecificationRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeployResourceSpecificationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeployResourceSpecificationRequest) ProtoMessage() {} + +func (x *DeployResourceSpecificationRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeployResourceSpecificationRequest.ProtoReflect.Descriptor instead. +func (*DeployResourceSpecificationRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{0} +} + +func (x *DeployResourceSpecificationRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *DeployResourceSpecificationRequest) GetDatastoreName() string { + if x != nil { + return x.DatastoreName + } + return "" +} + +func (x *DeployResourceSpecificationRequest) GetResources() []*ResourceSpecification { + if x != nil { + return x.Resources + } + return nil +} + +func (x *DeployResourceSpecificationRequest) GetNamespaceName() string { + if x != nil { + return x.NamespaceName + } + return "" +} + +type DeployResourceSpecificationResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + LogStatus *Log `protobuf:"bytes,5,opt,name=log_status,json=logStatus,proto3" json:"log_status,omitempty"` +} + +func (x *DeployResourceSpecificationResponse) Reset() { + *x = DeployResourceSpecificationResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeployResourceSpecificationResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeployResourceSpecificationResponse) ProtoMessage() {} + +func (x *DeployResourceSpecificationResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeployResourceSpecificationResponse.ProtoReflect.Descriptor instead. +func (*DeployResourceSpecificationResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{1} +} + +func (x *DeployResourceSpecificationResponse) GetLogStatus() *Log { + if x != nil { + return x.LogStatus + } + return nil +} + +// ListResourceSpecificationRequest lists all resource specifications of a datastore in project +type ListResourceSpecificationRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + DatastoreName string `protobuf:"bytes,2,opt,name=datastore_name,json=datastoreName,proto3" json:"datastore_name,omitempty"` + NamespaceName string `protobuf:"bytes,3,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` +} + +func (x *ListResourceSpecificationRequest) Reset() { + *x = ListResourceSpecificationRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListResourceSpecificationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListResourceSpecificationRequest) ProtoMessage() {} + +func (x *ListResourceSpecificationRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListResourceSpecificationRequest.ProtoReflect.Descriptor instead. +func (*ListResourceSpecificationRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{2} +} + +func (x *ListResourceSpecificationRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *ListResourceSpecificationRequest) GetDatastoreName() string { + if x != nil { + return x.DatastoreName + } + return "" +} + +func (x *ListResourceSpecificationRequest) GetNamespaceName() string { + if x != nil { + return x.NamespaceName + } + return "" +} + +type ListResourceSpecificationResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Resources []*ResourceSpecification `protobuf:"bytes,1,rep,name=resources,proto3" json:"resources,omitempty"` +} + +func (x *ListResourceSpecificationResponse) Reset() { + *x = ListResourceSpecificationResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListResourceSpecificationResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListResourceSpecificationResponse) ProtoMessage() {} + +func (x *ListResourceSpecificationResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListResourceSpecificationResponse.ProtoReflect.Descriptor instead. +func (*ListResourceSpecificationResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{3} +} + +func (x *ListResourceSpecificationResponse) GetResources() []*ResourceSpecification { + if x != nil { + return x.Resources + } + return nil +} + +type CreateResourceRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + DatastoreName string `protobuf:"bytes,2,opt,name=datastore_name,json=datastoreName,proto3" json:"datastore_name,omitempty"` + Resource *ResourceSpecification `protobuf:"bytes,3,opt,name=resource,proto3" json:"resource,omitempty"` + NamespaceName string `protobuf:"bytes,4,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` +} + +func (x *CreateResourceRequest) Reset() { + *x = CreateResourceRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateResourceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateResourceRequest) ProtoMessage() {} + +func (x *CreateResourceRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateResourceRequest.ProtoReflect.Descriptor instead. +func (*CreateResourceRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{4} +} + +func (x *CreateResourceRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *CreateResourceRequest) GetDatastoreName() string { + if x != nil { + return x.DatastoreName + } + return "" +} + +func (x *CreateResourceRequest) GetResource() *ResourceSpecification { + if x != nil { + return x.Resource + } + return nil +} + +func (x *CreateResourceRequest) GetNamespaceName() string { + if x != nil { + return x.NamespaceName + } + return "" +} + +type CreateResourceResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *CreateResourceResponse) Reset() { + *x = CreateResourceResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateResourceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateResourceResponse) ProtoMessage() {} + +func (x *CreateResourceResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateResourceResponse.ProtoReflect.Descriptor instead. +func (*CreateResourceResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{5} +} + +func (x *CreateResourceResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +func (x *CreateResourceResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +type ReadResourceRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + DatastoreName string `protobuf:"bytes,2,opt,name=datastore_name,json=datastoreName,proto3" json:"datastore_name,omitempty"` + ResourceName string `protobuf:"bytes,3,opt,name=resource_name,json=resourceName,proto3" json:"resource_name,omitempty"` + NamespaceName string `protobuf:"bytes,4,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` +} + +func (x *ReadResourceRequest) Reset() { + *x = ReadResourceRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReadResourceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReadResourceRequest) ProtoMessage() {} + +func (x *ReadResourceRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReadResourceRequest.ProtoReflect.Descriptor instead. +func (*ReadResourceRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{6} +} + +func (x *ReadResourceRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *ReadResourceRequest) GetDatastoreName() string { + if x != nil { + return x.DatastoreName + } + return "" +} + +func (x *ReadResourceRequest) GetResourceName() string { + if x != nil { + return x.ResourceName + } + return "" +} + +func (x *ReadResourceRequest) GetNamespaceName() string { + if x != nil { + return x.NamespaceName + } + return "" +} + +type ReadResourceResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + Resource *ResourceSpecification `protobuf:"bytes,3,opt,name=resource,proto3" json:"resource,omitempty"` +} + +func (x *ReadResourceResponse) Reset() { + *x = ReadResourceResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReadResourceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReadResourceResponse) ProtoMessage() {} + +func (x *ReadResourceResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReadResourceResponse.ProtoReflect.Descriptor instead. +func (*ReadResourceResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{7} +} + +func (x *ReadResourceResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +func (x *ReadResourceResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *ReadResourceResponse) GetResource() *ResourceSpecification { + if x != nil { + return x.Resource + } + return nil +} + +type UpdateResourceRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + DatastoreName string `protobuf:"bytes,2,opt,name=datastore_name,json=datastoreName,proto3" json:"datastore_name,omitempty"` + Resource *ResourceSpecification `protobuf:"bytes,3,opt,name=resource,proto3" json:"resource,omitempty"` + NamespaceName string `protobuf:"bytes,4,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` +} + +func (x *UpdateResourceRequest) Reset() { + *x = UpdateResourceRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateResourceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateResourceRequest) ProtoMessage() {} + +func (x *UpdateResourceRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateResourceRequest.ProtoReflect.Descriptor instead. +func (*UpdateResourceRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{8} +} + +func (x *UpdateResourceRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *UpdateResourceRequest) GetDatastoreName() string { + if x != nil { + return x.DatastoreName + } + return "" +} + +func (x *UpdateResourceRequest) GetResource() *ResourceSpecification { + if x != nil { + return x.Resource + } + return nil +} + +func (x *UpdateResourceRequest) GetNamespaceName() string { + if x != nil { + return x.NamespaceName + } + return "" +} + +type UpdateResourceResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *UpdateResourceResponse) Reset() { + *x = UpdateResourceResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateResourceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateResourceResponse) ProtoMessage() {} + +func (x *UpdateResourceResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateResourceResponse.ProtoReflect.Descriptor instead. +func (*UpdateResourceResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{9} +} + +func (x *UpdateResourceResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +func (x *UpdateResourceResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +// ResourceSpecification are datastore specification representation of a resource +type ResourceSpecification struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Version int32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Type string `protobuf:"bytes,4,opt,name=type,proto3" json:"type,omitempty"` + Spec *structpb.Struct `protobuf:"bytes,5,opt,name=spec,proto3" json:"spec,omitempty"` + Assets map[string]string `protobuf:"bytes,6,rep,name=assets,proto3" json:"assets,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Labels map[string]string `protobuf:"bytes,7,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *ResourceSpecification) Reset() { + *x = ResourceSpecification{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ResourceSpecification) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResourceSpecification) ProtoMessage() {} + +func (x *ResourceSpecification) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ResourceSpecification.ProtoReflect.Descriptor instead. +func (*ResourceSpecification) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{10} +} + +func (x *ResourceSpecification) GetVersion() int32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *ResourceSpecification) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ResourceSpecification) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *ResourceSpecification) GetSpec() *structpb.Struct { + if x != nil { + return x.Spec + } + return nil +} + +func (x *ResourceSpecification) GetAssets() map[string]string { + if x != nil { + return x.Assets + } + return nil +} + +func (x *ResourceSpecification) GetLabels() map[string]string { + if x != nil { + return x.Labels + } + return nil +} + +type ChangeResourceNamespaceRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + NamespaceName string `protobuf:"bytes,2,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` + DatastoreName string `protobuf:"bytes,3,opt,name=datastore_name,json=datastoreName,proto3" json:"datastore_name,omitempty"` + ResourceName string `protobuf:"bytes,4,opt,name=resource_name,json=resourceName,proto3" json:"resource_name,omitempty"` + NewNamespaceName string `protobuf:"bytes,5,opt,name=new_namespace_name,json=newNamespaceName,proto3" json:"new_namespace_name,omitempty"` +} + +func (x *ChangeResourceNamespaceRequest) Reset() { + *x = ChangeResourceNamespaceRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ChangeResourceNamespaceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ChangeResourceNamespaceRequest) ProtoMessage() {} + +func (x *ChangeResourceNamespaceRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ChangeResourceNamespaceRequest.ProtoReflect.Descriptor instead. +func (*ChangeResourceNamespaceRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{11} +} + +func (x *ChangeResourceNamespaceRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *ChangeResourceNamespaceRequest) GetNamespaceName() string { + if x != nil { + return x.NamespaceName + } + return "" +} + +func (x *ChangeResourceNamespaceRequest) GetDatastoreName() string { + if x != nil { + return x.DatastoreName + } + return "" +} + +func (x *ChangeResourceNamespaceRequest) GetResourceName() string { + if x != nil { + return x.ResourceName + } + return "" +} + +func (x *ChangeResourceNamespaceRequest) GetNewNamespaceName() string { + if x != nil { + return x.NewNamespaceName + } + return "" +} + +type ChangeResourceNamespaceResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ChangeResourceNamespaceResponse) Reset() { + *x = ChangeResourceNamespaceResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ChangeResourceNamespaceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ChangeResourceNamespaceResponse) ProtoMessage() {} + +func (x *ChangeResourceNamespaceResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ChangeResourceNamespaceResponse.ProtoReflect.Descriptor instead. +func (*ChangeResourceNamespaceResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{12} +} + +type ApplyResourcesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + NamespaceName string `protobuf:"bytes,2,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` + DatastoreName string `protobuf:"bytes,3,opt,name=datastore_name,json=datastoreName,proto3" json:"datastore_name,omitempty"` + ResourceNames []string `protobuf:"bytes,4,rep,name=resource_names,json=resourceNames,proto3" json:"resource_names,omitempty"` +} + +func (x *ApplyResourcesRequest) Reset() { + *x = ApplyResourcesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ApplyResourcesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ApplyResourcesRequest) ProtoMessage() {} + +func (x *ApplyResourcesRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ApplyResourcesRequest.ProtoReflect.Descriptor instead. +func (*ApplyResourcesRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{13} +} + +func (x *ApplyResourcesRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *ApplyResourcesRequest) GetNamespaceName() string { + if x != nil { + return x.NamespaceName + } + return "" +} + +func (x *ApplyResourcesRequest) GetDatastoreName() string { + if x != nil { + return x.DatastoreName + } + return "" +} + +func (x *ApplyResourcesRequest) GetResourceNames() []string { + if x != nil { + return x.ResourceNames + } + return nil +} + +type ApplyResourcesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Statuses []*ApplyResourcesResponse_ResourceStatus `protobuf:"bytes,1,rep,name=statuses,proto3" json:"statuses,omitempty"` +} + +func (x *ApplyResourcesResponse) Reset() { + *x = ApplyResourcesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ApplyResourcesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ApplyResourcesResponse) ProtoMessage() {} + +func (x *ApplyResourcesResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ApplyResourcesResponse.ProtoReflect.Descriptor instead. +func (*ApplyResourcesResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{14} +} + +func (x *ApplyResourcesResponse) GetStatuses() []*ApplyResourcesResponse_ResourceStatus { + if x != nil { + return x.Statuses + } + return nil +} + +type ApplyResourcesResponse_ResourceStatus struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ResourceName string `protobuf:"bytes,1,opt,name=resource_name,json=resourceName,proto3" json:"resource_name,omitempty"` + Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` + Reason string `protobuf:"bytes,3,opt,name=reason,proto3" json:"reason,omitempty"` +} + +func (x *ApplyResourcesResponse_ResourceStatus) Reset() { + *x = ApplyResourcesResponse_ResourceStatus{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ApplyResourcesResponse_ResourceStatus) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ApplyResourcesResponse_ResourceStatus) ProtoMessage() {} + +func (x *ApplyResourcesResponse_ResourceStatus) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ApplyResourcesResponse_ResourceStatus.ProtoReflect.Descriptor instead. +func (*ApplyResourcesResponse_ResourceStatus) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_resource_proto_rawDescGZIP(), []int{14, 0} +} + +func (x *ApplyResourcesResponse_ResourceStatus) GetResourceName() string { + if x != nil { + return x.ResourceName + } + return "" +} + +func (x *ApplyResourcesResponse_ResourceStatus) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *ApplyResourcesResponse_ResourceStatus) GetReason() string { + if x != nil { + return x.Reason + } + return "" +} + +var File_raystack_optimus_core_v1beta1_resource_proto protoreflect.FileDescriptor + +var file_raystack_optimus_core_v1beta1_resource_proto_rawDesc = []byte{ + 0x0a, 0x2f, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2f, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x20, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, + 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, + 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x2d, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2f, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, + 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, 0x6e, + 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xec, + 0x01, 0x0a, 0x22, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x61, 0x74, 0x61, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x55, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, + 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x71, 0x0a, + 0x23, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, + 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x5f, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x52, + 0x09, 0x6c, 0x6f, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x05, + 0x22, 0x93, 0x01, 0x0a, 0x20, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x61, 0x74, 0x61, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x7a, 0x0a, 0x21, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x09, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, + 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x22, 0xdd, 0x01, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x25, 0x0a, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x53, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x22, 0x4c, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, + 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x22, 0xab, 0x01, 0x0a, 0x13, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, + 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x9f, + 0x01, 0x0a, 0x14, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x53, 0x0a, 0x08, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x37, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, + 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x22, 0xdd, 0x01, 0x0a, 0x15, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, + 0x0e, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x53, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x22, 0x4c, 0x0a, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xbc, + 0x03, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x73, 0x70, + 0x65, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x5b, 0x0a, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x43, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x73, 0x12, 0x5b, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x07, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x43, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4c, 0x61, + 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, + 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x39, 0x0a, 0x0b, + 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0xe4, 0x01, + 0x0a, 0x1e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x61, + 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x6e, 0x65, 0x77, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x10, 0x6e, 0x65, 0x77, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x21, 0x0a, 0x1f, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xaf, 0x01, 0x0a, 0x15, 0x41, 0x70, 0x70, 0x6c, + 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, + 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0xe4, 0x01, 0x0a, 0x16, 0x41, 0x70, + 0x70, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x63, 0x0a, 0x08, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x47, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x08, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x65, 0x73, 0x1a, 0x65, 0x0a, 0x0e, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, + 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, + 0x32, 0x8c, 0x0d, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x12, 0xb0, 0x01, 0x0a, 0x1b, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x44, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x45, 0x2e, 0x67, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, + 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, + 0x70, 0x6c, 0x6f, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0x8c, 0x02, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x42, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x43, 0x2e, 0x67, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x66, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x60, 0x12, 0x5e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x7d, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x7b, 0x64, 0x61, + 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0xee, 0x01, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x37, 0x2e, 0x67, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x69, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x63, 0x22, 0x5e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, + 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, + 0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x7b, 0x64, 0x61, 0x74, 0x61, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0xf5, 0x01, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x35, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x64, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x76, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x70, 0x12, + 0x6e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, + 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x64, 0x61, 0x74, 0x61, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x7b, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2f, + 0x7b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, + 0xee, 0x01, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x12, 0x37, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x67, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, + 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x69, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x63, 0x1a, 0x5e, 0x2f, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, + 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x2f, 0x7b, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x3a, 0x01, 0x2a, + 0x12, 0xe4, 0x01, 0x0a, 0x17, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x40, 0x2e, 0x67, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x41, + 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x44, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3e, 0x22, 0x39, 0x2f, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x2d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0xf5, 0x01, 0x0a, 0x0e, 0x41, 0x70, 0x70, 0x6c, + 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x37, 0x2e, 0x67, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, + 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, 0x70, + 0x70, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, + 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x70, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x6a, 0x22, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x7d, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x7b, 0x64, 0x61, 0x74, + 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2d, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x3a, 0x01, 0x2a, 0x42, + 0xa4, 0x01, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x75, 0x73, 0x42, 0x16, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x1e, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x6e, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x92, 0x41, 0x47, 0x12, + 0x05, 0x32, 0x03, 0x30, 0x2e, 0x31, 0x1a, 0x0e, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, + 0x31, 0x3a, 0x39, 0x31, 0x30, 0x30, 0x22, 0x04, 0x2f, 0x61, 0x70, 0x69, 0x2a, 0x01, 0x01, 0x72, + 0x25, 0x0a, 0x23, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x20, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x20, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_raystack_optimus_core_v1beta1_resource_proto_rawDescOnce sync.Once + file_raystack_optimus_core_v1beta1_resource_proto_rawDescData = file_raystack_optimus_core_v1beta1_resource_proto_rawDesc +) + +func file_raystack_optimus_core_v1beta1_resource_proto_rawDescGZIP() []byte { + file_raystack_optimus_core_v1beta1_resource_proto_rawDescOnce.Do(func() { + file_raystack_optimus_core_v1beta1_resource_proto_rawDescData = protoimpl.X.CompressGZIP(file_raystack_optimus_core_v1beta1_resource_proto_rawDescData) + }) + return file_raystack_optimus_core_v1beta1_resource_proto_rawDescData +} + +var file_raystack_optimus_core_v1beta1_resource_proto_msgTypes = make([]protoimpl.MessageInfo, 18) +var file_raystack_optimus_core_v1beta1_resource_proto_goTypes = []interface{}{ + (*DeployResourceSpecificationRequest)(nil), // 0: raystack.optimus.core.v1beta1.DeployResourceSpecificationRequest + (*DeployResourceSpecificationResponse)(nil), // 1: raystack.optimus.core.v1beta1.DeployResourceSpecificationResponse + (*ListResourceSpecificationRequest)(nil), // 2: raystack.optimus.core.v1beta1.ListResourceSpecificationRequest + (*ListResourceSpecificationResponse)(nil), // 3: raystack.optimus.core.v1beta1.ListResourceSpecificationResponse + (*CreateResourceRequest)(nil), // 4: raystack.optimus.core.v1beta1.CreateResourceRequest + (*CreateResourceResponse)(nil), // 5: raystack.optimus.core.v1beta1.CreateResourceResponse + (*ReadResourceRequest)(nil), // 6: raystack.optimus.core.v1beta1.ReadResourceRequest + (*ReadResourceResponse)(nil), // 7: raystack.optimus.core.v1beta1.ReadResourceResponse + (*UpdateResourceRequest)(nil), // 8: raystack.optimus.core.v1beta1.UpdateResourceRequest + (*UpdateResourceResponse)(nil), // 9: raystack.optimus.core.v1beta1.UpdateResourceResponse + (*ResourceSpecification)(nil), // 10: raystack.optimus.core.v1beta1.ResourceSpecification + (*ChangeResourceNamespaceRequest)(nil), // 11: raystack.optimus.core.v1beta1.ChangeResourceNamespaceRequest + (*ChangeResourceNamespaceResponse)(nil), // 12: raystack.optimus.core.v1beta1.ChangeResourceNamespaceResponse + (*ApplyResourcesRequest)(nil), // 13: raystack.optimus.core.v1beta1.ApplyResourcesRequest + (*ApplyResourcesResponse)(nil), // 14: raystack.optimus.core.v1beta1.ApplyResourcesResponse + nil, // 15: raystack.optimus.core.v1beta1.ResourceSpecification.AssetsEntry + nil, // 16: raystack.optimus.core.v1beta1.ResourceSpecification.LabelsEntry + (*ApplyResourcesResponse_ResourceStatus)(nil), // 17: raystack.optimus.core.v1beta1.ApplyResourcesResponse.ResourceStatus + (*Log)(nil), // 18: raystack.optimus.core.v1beta1.Log + (*structpb.Struct)(nil), // 19: google.protobuf.Struct +} +var file_raystack_optimus_core_v1beta1_resource_proto_depIdxs = []int32{ + 10, // 0: raystack.optimus.core.v1beta1.DeployResourceSpecificationRequest.resources:type_name -> raystack.optimus.core.v1beta1.ResourceSpecification + 18, // 1: raystack.optimus.core.v1beta1.DeployResourceSpecificationResponse.log_status:type_name -> raystack.optimus.core.v1beta1.Log + 10, // 2: raystack.optimus.core.v1beta1.ListResourceSpecificationResponse.resources:type_name -> raystack.optimus.core.v1beta1.ResourceSpecification + 10, // 3: raystack.optimus.core.v1beta1.CreateResourceRequest.resource:type_name -> raystack.optimus.core.v1beta1.ResourceSpecification + 10, // 4: raystack.optimus.core.v1beta1.ReadResourceResponse.resource:type_name -> raystack.optimus.core.v1beta1.ResourceSpecification + 10, // 5: raystack.optimus.core.v1beta1.UpdateResourceRequest.resource:type_name -> raystack.optimus.core.v1beta1.ResourceSpecification + 19, // 6: raystack.optimus.core.v1beta1.ResourceSpecification.spec:type_name -> google.protobuf.Struct + 15, // 7: raystack.optimus.core.v1beta1.ResourceSpecification.assets:type_name -> raystack.optimus.core.v1beta1.ResourceSpecification.AssetsEntry + 16, // 8: raystack.optimus.core.v1beta1.ResourceSpecification.labels:type_name -> raystack.optimus.core.v1beta1.ResourceSpecification.LabelsEntry + 17, // 9: raystack.optimus.core.v1beta1.ApplyResourcesResponse.statuses:type_name -> raystack.optimus.core.v1beta1.ApplyResourcesResponse.ResourceStatus + 0, // 10: raystack.optimus.core.v1beta1.ResourceService.DeployResourceSpecification:input_type -> raystack.optimus.core.v1beta1.DeployResourceSpecificationRequest + 2, // 11: raystack.optimus.core.v1beta1.ResourceService.ListResourceSpecification:input_type -> raystack.optimus.core.v1beta1.ListResourceSpecificationRequest + 4, // 12: raystack.optimus.core.v1beta1.ResourceService.CreateResource:input_type -> raystack.optimus.core.v1beta1.CreateResourceRequest + 6, // 13: raystack.optimus.core.v1beta1.ResourceService.ReadResource:input_type -> raystack.optimus.core.v1beta1.ReadResourceRequest + 8, // 14: raystack.optimus.core.v1beta1.ResourceService.UpdateResource:input_type -> raystack.optimus.core.v1beta1.UpdateResourceRequest + 11, // 15: raystack.optimus.core.v1beta1.ResourceService.ChangeResourceNamespace:input_type -> raystack.optimus.core.v1beta1.ChangeResourceNamespaceRequest + 13, // 16: raystack.optimus.core.v1beta1.ResourceService.ApplyResources:input_type -> raystack.optimus.core.v1beta1.ApplyResourcesRequest + 1, // 17: raystack.optimus.core.v1beta1.ResourceService.DeployResourceSpecification:output_type -> raystack.optimus.core.v1beta1.DeployResourceSpecificationResponse + 3, // 18: raystack.optimus.core.v1beta1.ResourceService.ListResourceSpecification:output_type -> raystack.optimus.core.v1beta1.ListResourceSpecificationResponse + 5, // 19: raystack.optimus.core.v1beta1.ResourceService.CreateResource:output_type -> raystack.optimus.core.v1beta1.CreateResourceResponse + 7, // 20: raystack.optimus.core.v1beta1.ResourceService.ReadResource:output_type -> raystack.optimus.core.v1beta1.ReadResourceResponse + 9, // 21: raystack.optimus.core.v1beta1.ResourceService.UpdateResource:output_type -> raystack.optimus.core.v1beta1.UpdateResourceResponse + 12, // 22: raystack.optimus.core.v1beta1.ResourceService.ChangeResourceNamespace:output_type -> raystack.optimus.core.v1beta1.ChangeResourceNamespaceResponse + 14, // 23: raystack.optimus.core.v1beta1.ResourceService.ApplyResources:output_type -> raystack.optimus.core.v1beta1.ApplyResourcesResponse + 17, // [17:24] is the sub-list for method output_type + 10, // [10:17] is the sub-list for method input_type + 10, // [10:10] is the sub-list for extension type_name + 10, // [10:10] is the sub-list for extension extendee + 0, // [0:10] is the sub-list for field type_name +} + +func init() { file_raystack_optimus_core_v1beta1_resource_proto_init() } +func file_raystack_optimus_core_v1beta1_resource_proto_init() { + if File_raystack_optimus_core_v1beta1_resource_proto != nil { + return + } + file_raystack_optimus_core_v1beta1_status_proto_init() + if !protoimpl.UnsafeEnabled { + file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeployResourceSpecificationRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeployResourceSpecificationResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListResourceSpecificationRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListResourceSpecificationResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateResourceRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateResourceResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadResourceRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadResourceResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateResourceRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateResourceResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResourceSpecification); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ChangeResourceNamespaceRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ChangeResourceNamespaceResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ApplyResourcesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ApplyResourcesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_resource_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ApplyResourcesResponse_ResourceStatus); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_raystack_optimus_core_v1beta1_resource_proto_rawDesc, + NumEnums: 0, + NumMessages: 18, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_raystack_optimus_core_v1beta1_resource_proto_goTypes, + DependencyIndexes: file_raystack_optimus_core_v1beta1_resource_proto_depIdxs, + MessageInfos: file_raystack_optimus_core_v1beta1_resource_proto_msgTypes, + }.Build() + File_raystack_optimus_core_v1beta1_resource_proto = out.File + file_raystack_optimus_core_v1beta1_resource_proto_rawDesc = nil + file_raystack_optimus_core_v1beta1_resource_proto_goTypes = nil + file_raystack_optimus_core_v1beta1_resource_proto_depIdxs = nil +} diff --git a/protos/odpf/optimus/core/v1beta1/resource.pb.gw.go b/protos/raystack/optimus/core/v1beta1/resource.pb.gw.go similarity index 66% rename from protos/odpf/optimus/core/v1beta1/resource.pb.gw.go rename to protos/raystack/optimus/core/v1beta1/resource.pb.gw.go index a696ec1930..816b3ce15b 100644 --- a/protos/odpf/optimus/core/v1beta1/resource.pb.gw.go +++ b/protos/raystack/optimus/core/v1beta1/resource.pb.gw.go @@ -1,5 +1,5 @@ // Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. -// source: odpf/optimus/core/v1beta1/resource.proto +// source: raystack/optimus/core/v1beta1/resource.proto /* Package optimus is a reverse proxy. @@ -451,6 +451,182 @@ func local_request_ResourceService_UpdateResource_0(ctx context.Context, marshal } +func request_ResourceService_ChangeResourceNamespace_0(ctx context.Context, marshaler runtime.Marshaler, client ResourceServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ChangeResourceNamespaceRequest + 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); 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["project_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "project_name") + } + + protoReq.ProjectName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "project_name", err) + } + + msg, err := client.ChangeResourceNamespace(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_ResourceService_ChangeResourceNamespace_0(ctx context.Context, marshaler runtime.Marshaler, server ResourceServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ChangeResourceNamespaceRequest + 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); 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["project_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "project_name") + } + + protoReq.ProjectName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "project_name", err) + } + + msg, err := server.ChangeResourceNamespace(ctx, &protoReq) + return msg, metadata, err + +} + +func request_ResourceService_ApplyResources_0(ctx context.Context, marshaler runtime.Marshaler, client ResourceServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ApplyResourcesRequest + 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); 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["project_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "project_name") + } + + protoReq.ProjectName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "project_name", err) + } + + val, ok = pathParams["namespace_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "namespace_name") + } + + protoReq.NamespaceName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "namespace_name", err) + } + + val, ok = pathParams["datastore_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "datastore_name") + } + + protoReq.DatastoreName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "datastore_name", err) + } + + msg, err := client.ApplyResources(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_ResourceService_ApplyResources_0(ctx context.Context, marshaler runtime.Marshaler, server ResourceServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ApplyResourcesRequest + 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); 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["project_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "project_name") + } + + protoReq.ProjectName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "project_name", err) + } + + val, ok = pathParams["namespace_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "namespace_name") + } + + protoReq.NamespaceName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "namespace_name", err) + } + + val, ok = pathParams["datastore_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "datastore_name") + } + + protoReq.DatastoreName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "datastore_name", err) + } + + msg, err := server.ApplyResources(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterResourceServiceHandlerServer registers the http handlers for service ResourceService to "mux". // UnaryRPC :call ResourceServiceServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -463,7 +639,7 @@ func RegisterResourceServiceHandlerServer(ctx context.Context, mux *runtime.Serv var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.ResourceService/ListResourceSpecification", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/resource")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.ResourceService/ListResourceSpecification", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/resource")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -486,7 +662,7 @@ func RegisterResourceServiceHandlerServer(ctx context.Context, mux *runtime.Serv var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.ResourceService/CreateResource", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/resource")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.ResourceService/CreateResource", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/resource")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -509,7 +685,7 @@ func RegisterResourceServiceHandlerServer(ctx context.Context, mux *runtime.Serv var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.ResourceService/ReadResource", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/resource/{resource_name}")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.ResourceService/ReadResource", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/resource/{resource_name}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -532,7 +708,7 @@ func RegisterResourceServiceHandlerServer(ctx context.Context, mux *runtime.Serv var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.ResourceService/UpdateResource", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/resource")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.ResourceService/UpdateResource", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/resource")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -549,6 +725,52 @@ func RegisterResourceServiceHandlerServer(ctx context.Context, mux *runtime.Serv }) + mux.Handle("POST", pattern_ResourceService_ChangeResourceNamespace_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.ResourceService/ChangeResourceNamespace", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/change-resource-namespace")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_ResourceService_ChangeResourceNamespace_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_ResourceService_ChangeResourceNamespace_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_ResourceService_ApplyResources_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.ResourceService/ApplyResources", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/resources-apply")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_ResourceService_ApplyResources_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_ResourceService_ApplyResources_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -594,7 +816,7 @@ func RegisterResourceServiceHandlerClient(ctx context.Context, mux *runtime.Serv ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.ResourceService/ListResourceSpecification", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/resource")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.ResourceService/ListResourceSpecification", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/resource")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -614,7 +836,7 @@ func RegisterResourceServiceHandlerClient(ctx context.Context, mux *runtime.Serv ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.ResourceService/CreateResource", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/resource")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.ResourceService/CreateResource", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/resource")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -634,7 +856,7 @@ func RegisterResourceServiceHandlerClient(ctx context.Context, mux *runtime.Serv ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.ResourceService/ReadResource", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/resource/{resource_name}")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.ResourceService/ReadResource", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/resource/{resource_name}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -654,7 +876,7 @@ func RegisterResourceServiceHandlerClient(ctx context.Context, mux *runtime.Serv ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.ResourceService/UpdateResource", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/resource")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.ResourceService/UpdateResource", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/resource")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -670,6 +892,46 @@ func RegisterResourceServiceHandlerClient(ctx context.Context, mux *runtime.Serv }) + mux.Handle("POST", pattern_ResourceService_ChangeResourceNamespace_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, "/raystack.optimus.core.v1beta1.ResourceService/ChangeResourceNamespace", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/change-resource-namespace")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_ResourceService_ChangeResourceNamespace_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_ResourceService_ChangeResourceNamespace_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_ResourceService_ApplyResources_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, "/raystack.optimus.core.v1beta1.ResourceService/ApplyResources", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/namespace/{namespace_name}/datastore/{datastore_name}/resources-apply")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_ResourceService_ApplyResources_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_ResourceService_ApplyResources_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -681,6 +943,10 @@ var ( pattern_ResourceService_ReadResource_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, 2, 7, 1, 0, 4, 1, 5, 8}, []string{"v1beta1", "project", "project_name", "namespace", "namespace_name", "datastore", "datastore_name", "resource", "resource_name"}, "")) pattern_ResourceService_UpdateResource_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, 2, 7}, []string{"v1beta1", "project", "project_name", "namespace", "namespace_name", "datastore", "datastore_name", "resource"}, "")) + + pattern_ResourceService_ChangeResourceNamespace_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"v1beta1", "project", "project_name", "change-resource-namespace"}, "")) + + pattern_ResourceService_ApplyResources_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, 2, 7}, []string{"v1beta1", "project", "project_name", "namespace", "namespace_name", "datastore", "datastore_name", "resources-apply"}, "")) ) var ( @@ -691,4 +957,8 @@ var ( forward_ResourceService_ReadResource_0 = runtime.ForwardResponseMessage forward_ResourceService_UpdateResource_0 = runtime.ForwardResponseMessage + + forward_ResourceService_ChangeResourceNamespace_0 = runtime.ForwardResponseMessage + + forward_ResourceService_ApplyResources_0 = runtime.ForwardResponseMessage ) diff --git a/protos/odpf/optimus/core/v1beta1/resource.swagger.json b/protos/raystack/optimus/core/v1beta1/resource.swagger.json similarity index 69% rename from protos/odpf/optimus/core/v1beta1/resource.swagger.json rename to protos/raystack/optimus/core/v1beta1/resource.swagger.json index 1811909197..3b202e08ac 100644 --- a/protos/odpf/optimus/core/v1beta1/resource.swagger.json +++ b/protos/raystack/optimus/core/v1beta1/resource.swagger.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "title": "odpf/optimus/core/v1beta1/resource.proto", + "title": "raystack/optimus/core/v1beta1/resource.proto", "version": "0.1" }, "tags": [ @@ -11,16 +11,61 @@ ], "host": "127.0.0.1:9100", "basePath": "/api", - "schemes": [ - "http" - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], + "schemes": ["http"], + "consumes": ["application/json"], + "produces": ["application/json"], "paths": { + "/v1beta1/project/{projectName}/change-resource-namespace": { + "post": { + "summary": "ChangeJobNamespace move a job spec from one namespace to another", + "operationId": "ResourceService_ChangeResourceNamespace", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1beta1ChangeResourceNamespaceResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "projectName", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "namespaceName": { + "type": "string" + }, + "datastoreName": { + "type": "string" + }, + "resourceName": { + "type": "string" + }, + "newNamespaceName": { + "type": "string" + } + } + } + } + ], + "tags": ["ResourceService"] + } + }, "/v1beta1/project/{projectName}/namespace/{namespaceName}/datastore/{datastoreName}/resource": { "get": { "summary": "ListResourceSpecification lists all resource specifications of a datastore in project", @@ -59,9 +104,7 @@ "type": "string" } ], - "tags": [ - "ResourceService" - ] + "tags": ["ResourceService"] }, "post": { "summary": "Database CRUD\nCreateResource registers a new resource of a namespace which belongs to a project", @@ -113,9 +156,7 @@ } } ], - "tags": [ - "ResourceService" - ] + "tags": ["ResourceService"] }, "put": { "summary": "UpdateResource updates a resource specification of a datastore in project", @@ -167,9 +208,7 @@ } } ], - "tags": [ - "ResourceService" - ] + "tags": ["ResourceService"] } }, "/v1beta1/project/{projectName}/namespace/{namespaceName}/datastore/{datastoreName}/resource/{resourceName}": { @@ -216,13 +255,82 @@ "type": "string" } ], - "tags": [ - "ResourceService" - ] + "tags": ["ResourceService"] + } + }, + "/v1beta1/project/{projectName}/namespace/{namespaceName}/datastore/{datastoreName}/resources-apply": { + "post": { + "summary": "apply a resource from optimus to datastore", + "operationId": "ResourceService_ApplyResources", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1beta1ApplyResourcesResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "projectName", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "namespaceName", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "datastoreName", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "resourceNames": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + ], + "tags": ["ResourceService"] } } }, "definitions": { + "ApplyResourcesResponseResourceStatus": { + "type": "object", + "properties": { + "resourceName": { + "type": "string" + }, + "status": { + "type": "string" + }, + "reason": { + "type": "string" + } + } + }, "protobufAny": { "type": "object", "properties": { @@ -237,9 +345,7 @@ }, "protobufNullValue": { "type": "string", - "enum": [ - "NULL_VALUE" - ], + "enum": ["NULL_VALUE"], "default": "NULL_VALUE", "description": "`NullValue` is a singleton enumeration to represent the null value for the\n`Value` type union.\n\n The JSON representation for `NullValue` is JSON `null`.\n\n - NULL_VALUE: Null value." }, @@ -261,6 +367,20 @@ } } }, + "v1beta1ApplyResourcesResponse": { + "type": "object", + "properties": { + "statuses": { + "type": "array", + "items": { + "$ref": "#/definitions/ApplyResourcesResponseResourceStatus" + } + } + } + }, + "v1beta1ChangeResourceNamespaceResponse": { + "type": "object" + }, "v1beta1CreateResourceResponse": { "type": "object", "properties": { diff --git a/protos/odpf/optimus/core/v1beta1/resource_grpc.pb.go b/protos/raystack/optimus/core/v1beta1/resource_grpc.pb.go similarity index 71% rename from protos/odpf/optimus/core/v1beta1/resource_grpc.pb.go rename to protos/raystack/optimus/core/v1beta1/resource_grpc.pb.go index 72d6bf37b3..4f43dc88dc 100644 --- a/protos/odpf/optimus/core/v1beta1/resource_grpc.pb.go +++ b/protos/raystack/optimus/core/v1beta1/resource_grpc.pb.go @@ -2,7 +2,7 @@ // versions: // - protoc-gen-go-grpc v1.2.0 // - protoc (unknown) -// source: odpf/optimus/core/v1beta1/resource.proto +// source: raystack/optimus/core/v1beta1/resource.proto package optimus @@ -34,6 +34,10 @@ type ResourceServiceClient interface { ReadResource(ctx context.Context, in *ReadResourceRequest, opts ...grpc.CallOption) (*ReadResourceResponse, error) // UpdateResource updates a resource specification of a datastore in project UpdateResource(ctx context.Context, in *UpdateResourceRequest, opts ...grpc.CallOption) (*UpdateResourceResponse, error) + // ChangeJobNamespace move a job spec from one namespace to another + ChangeResourceNamespace(ctx context.Context, in *ChangeResourceNamespaceRequest, opts ...grpc.CallOption) (*ChangeResourceNamespaceResponse, error) + // apply a resource from optimus to datastore + ApplyResources(ctx context.Context, in *ApplyResourcesRequest, opts ...grpc.CallOption) (*ApplyResourcesResponse, error) } type resourceServiceClient struct { @@ -45,7 +49,7 @@ func NewResourceServiceClient(cc grpc.ClientConnInterface) ResourceServiceClient } func (c *resourceServiceClient) DeployResourceSpecification(ctx context.Context, opts ...grpc.CallOption) (ResourceService_DeployResourceSpecificationClient, error) { - stream, err := c.cc.NewStream(ctx, &ResourceService_ServiceDesc.Streams[0], "/odpf.optimus.core.v1beta1.ResourceService/DeployResourceSpecification", opts...) + stream, err := c.cc.NewStream(ctx, &ResourceService_ServiceDesc.Streams[0], "/raystack.optimus.core.v1beta1.ResourceService/DeployResourceSpecification", opts...) if err != nil { return nil, err } @@ -77,7 +81,7 @@ func (x *resourceServiceDeployResourceSpecificationClient) Recv() (*DeployResour func (c *resourceServiceClient) ListResourceSpecification(ctx context.Context, in *ListResourceSpecificationRequest, opts ...grpc.CallOption) (*ListResourceSpecificationResponse, error) { out := new(ListResourceSpecificationResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.ResourceService/ListResourceSpecification", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.ResourceService/ListResourceSpecification", in, out, opts...) if err != nil { return nil, err } @@ -86,7 +90,7 @@ func (c *resourceServiceClient) ListResourceSpecification(ctx context.Context, i func (c *resourceServiceClient) CreateResource(ctx context.Context, in *CreateResourceRequest, opts ...grpc.CallOption) (*CreateResourceResponse, error) { out := new(CreateResourceResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.ResourceService/CreateResource", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.ResourceService/CreateResource", in, out, opts...) if err != nil { return nil, err } @@ -95,7 +99,7 @@ func (c *resourceServiceClient) CreateResource(ctx context.Context, in *CreateRe func (c *resourceServiceClient) ReadResource(ctx context.Context, in *ReadResourceRequest, opts ...grpc.CallOption) (*ReadResourceResponse, error) { out := new(ReadResourceResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.ResourceService/ReadResource", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.ResourceService/ReadResource", in, out, opts...) if err != nil { return nil, err } @@ -104,7 +108,25 @@ func (c *resourceServiceClient) ReadResource(ctx context.Context, in *ReadResour func (c *resourceServiceClient) UpdateResource(ctx context.Context, in *UpdateResourceRequest, opts ...grpc.CallOption) (*UpdateResourceResponse, error) { out := new(UpdateResourceResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.ResourceService/UpdateResource", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.ResourceService/UpdateResource", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *resourceServiceClient) ChangeResourceNamespace(ctx context.Context, in *ChangeResourceNamespaceRequest, opts ...grpc.CallOption) (*ChangeResourceNamespaceResponse, error) { + out := new(ChangeResourceNamespaceResponse) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.ResourceService/ChangeResourceNamespace", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *resourceServiceClient) ApplyResources(ctx context.Context, in *ApplyResourcesRequest, opts ...grpc.CallOption) (*ApplyResourcesResponse, error) { + out := new(ApplyResourcesResponse) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.ResourceService/ApplyResources", in, out, opts...) if err != nil { return nil, err } @@ -127,6 +149,10 @@ type ResourceServiceServer interface { ReadResource(context.Context, *ReadResourceRequest) (*ReadResourceResponse, error) // UpdateResource updates a resource specification of a datastore in project UpdateResource(context.Context, *UpdateResourceRequest) (*UpdateResourceResponse, error) + // ChangeJobNamespace move a job spec from one namespace to another + ChangeResourceNamespace(context.Context, *ChangeResourceNamespaceRequest) (*ChangeResourceNamespaceResponse, error) + // apply a resource from optimus to datastore + ApplyResources(context.Context, *ApplyResourcesRequest) (*ApplyResourcesResponse, error) mustEmbedUnimplementedResourceServiceServer() } @@ -149,6 +175,12 @@ func (UnimplementedResourceServiceServer) ReadResource(context.Context, *ReadRes func (UnimplementedResourceServiceServer) UpdateResource(context.Context, *UpdateResourceRequest) (*UpdateResourceResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method UpdateResource not implemented") } +func (UnimplementedResourceServiceServer) ChangeResourceNamespace(context.Context, *ChangeResourceNamespaceRequest) (*ChangeResourceNamespaceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ChangeResourceNamespace not implemented") +} +func (UnimplementedResourceServiceServer) ApplyResources(context.Context, *ApplyResourcesRequest) (*ApplyResourcesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ApplyResources not implemented") +} func (UnimplementedResourceServiceServer) mustEmbedUnimplementedResourceServiceServer() {} // UnsafeResourceServiceServer may be embedded to opt out of forward compatibility for this service. @@ -198,7 +230,7 @@ func _ResourceService_ListResourceSpecification_Handler(srv interface{}, ctx con } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.ResourceService/ListResourceSpecification", + FullMethod: "/raystack.optimus.core.v1beta1.ResourceService/ListResourceSpecification", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ResourceServiceServer).ListResourceSpecification(ctx, req.(*ListResourceSpecificationRequest)) @@ -216,7 +248,7 @@ func _ResourceService_CreateResource_Handler(srv interface{}, ctx context.Contex } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.ResourceService/CreateResource", + FullMethod: "/raystack.optimus.core.v1beta1.ResourceService/CreateResource", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ResourceServiceServer).CreateResource(ctx, req.(*CreateResourceRequest)) @@ -234,7 +266,7 @@ func _ResourceService_ReadResource_Handler(srv interface{}, ctx context.Context, } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.ResourceService/ReadResource", + FullMethod: "/raystack.optimus.core.v1beta1.ResourceService/ReadResource", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ResourceServiceServer).ReadResource(ctx, req.(*ReadResourceRequest)) @@ -252,7 +284,7 @@ func _ResourceService_UpdateResource_Handler(srv interface{}, ctx context.Contex } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.ResourceService/UpdateResource", + FullMethod: "/raystack.optimus.core.v1beta1.ResourceService/UpdateResource", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ResourceServiceServer).UpdateResource(ctx, req.(*UpdateResourceRequest)) @@ -260,11 +292,47 @@ func _ResourceService_UpdateResource_Handler(srv interface{}, ctx context.Contex return interceptor(ctx, in, info, handler) } +func _ResourceService_ChangeResourceNamespace_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ChangeResourceNamespaceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ResourceServiceServer).ChangeResourceNamespace(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/raystack.optimus.core.v1beta1.ResourceService/ChangeResourceNamespace", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ResourceServiceServer).ChangeResourceNamespace(ctx, req.(*ChangeResourceNamespaceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ResourceService_ApplyResources_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ApplyResourcesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ResourceServiceServer).ApplyResources(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/raystack.optimus.core.v1beta1.ResourceService/ApplyResources", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ResourceServiceServer).ApplyResources(ctx, req.(*ApplyResourcesRequest)) + } + return interceptor(ctx, in, info, handler) +} + // ResourceService_ServiceDesc is the grpc.ServiceDesc for ResourceService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var ResourceService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "odpf.optimus.core.v1beta1.ResourceService", + ServiceName: "raystack.optimus.core.v1beta1.ResourceService", HandlerType: (*ResourceServiceServer)(nil), Methods: []grpc.MethodDesc{ { @@ -283,6 +351,14 @@ var ResourceService_ServiceDesc = grpc.ServiceDesc{ MethodName: "UpdateResource", Handler: _ResourceService_UpdateResource_Handler, }, + { + MethodName: "ChangeResourceNamespace", + Handler: _ResourceService_ChangeResourceNamespace_Handler, + }, + { + MethodName: "ApplyResources", + Handler: _ResourceService_ApplyResources_Handler, + }, }, Streams: []grpc.StreamDesc{ { @@ -292,5 +368,5 @@ var ResourceService_ServiceDesc = grpc.ServiceDesc{ ClientStreams: true, }, }, - Metadata: "odpf/optimus/core/v1beta1/resource.proto", + Metadata: "raystack/optimus/core/v1beta1/resource.proto", } diff --git a/protos/raystack/optimus/core/v1beta1/runtime.pb.go b/protos/raystack/optimus/core/v1beta1/runtime.pb.go new file mode 100644 index 0000000000..90d0a2f2cd --- /dev/null +++ b/protos/raystack/optimus/core/v1beta1/runtime.pb.go @@ -0,0 +1,236 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.0 +// protoc (unknown) +// source: raystack/optimus/core/v1beta1/runtime.proto + +package optimus + +import ( + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type VersionRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Client string `protobuf:"bytes,1,opt,name=client,proto3" json:"client,omitempty"` +} + +func (x *VersionRequest) Reset() { + *x = VersionRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_runtime_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *VersionRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VersionRequest) ProtoMessage() {} + +func (x *VersionRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_runtime_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use VersionRequest.ProtoReflect.Descriptor instead. +func (*VersionRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_runtime_proto_rawDescGZIP(), []int{0} +} + +func (x *VersionRequest) GetClient() string { + if x != nil { + return x.Client + } + return "" +} + +type VersionResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Server string `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"` +} + +func (x *VersionResponse) Reset() { + *x = VersionResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_runtime_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *VersionResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VersionResponse) ProtoMessage() {} + +func (x *VersionResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_runtime_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use VersionResponse.ProtoReflect.Descriptor instead. +func (*VersionResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_runtime_proto_rawDescGZIP(), []int{1} +} + +func (x *VersionResponse) GetServer() string { + if x != nil { + return x.Server + } + return "" +} + +var File_raystack_optimus_core_v1beta1_runtime_proto protoreflect.FileDescriptor + +var file_raystack_optimus_core_v1beta1_runtime_proto_rawDesc = []byte{ + 0x0a, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2f, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x20, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, + 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, + 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, + 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x22, 0x28, 0x0a, 0x0e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x22, 0x29, 0x0a, 0x0f, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x32, 0x9e, 0x01, 0x0a, 0x0e, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x8b, 0x01, 0x0a, 0x07, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x15, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x3a, 0x01, 0x2a, 0x42, 0x97, 0x01, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x67, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x6e, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x42, 0x15, 0x52, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, + 0x50, 0x01, 0x5a, 0x1e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, + 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x75, 0x73, 0x92, 0x41, 0x3b, 0x12, 0x05, 0x32, 0x03, 0x30, 0x2e, 0x31, 0x1a, 0x0e, 0x31, 0x32, + 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x3a, 0x39, 0x31, 0x30, 0x30, 0x22, 0x04, 0x2f, 0x61, + 0x70, 0x69, 0x2a, 0x01, 0x01, 0x72, 0x19, 0x0a, 0x17, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, + 0x20, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_raystack_optimus_core_v1beta1_runtime_proto_rawDescOnce sync.Once + file_raystack_optimus_core_v1beta1_runtime_proto_rawDescData = file_raystack_optimus_core_v1beta1_runtime_proto_rawDesc +) + +func file_raystack_optimus_core_v1beta1_runtime_proto_rawDescGZIP() []byte { + file_raystack_optimus_core_v1beta1_runtime_proto_rawDescOnce.Do(func() { + file_raystack_optimus_core_v1beta1_runtime_proto_rawDescData = protoimpl.X.CompressGZIP(file_raystack_optimus_core_v1beta1_runtime_proto_rawDescData) + }) + return file_raystack_optimus_core_v1beta1_runtime_proto_rawDescData +} + +var file_raystack_optimus_core_v1beta1_runtime_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_raystack_optimus_core_v1beta1_runtime_proto_goTypes = []interface{}{ + (*VersionRequest)(nil), // 0: raystack.optimus.core.v1beta1.VersionRequest + (*VersionResponse)(nil), // 1: raystack.optimus.core.v1beta1.VersionResponse +} +var file_raystack_optimus_core_v1beta1_runtime_proto_depIdxs = []int32{ + 0, // 0: raystack.optimus.core.v1beta1.RuntimeService.Version:input_type -> raystack.optimus.core.v1beta1.VersionRequest + 1, // 1: raystack.optimus.core.v1beta1.RuntimeService.Version:output_type -> raystack.optimus.core.v1beta1.VersionResponse + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_raystack_optimus_core_v1beta1_runtime_proto_init() } +func file_raystack_optimus_core_v1beta1_runtime_proto_init() { + if File_raystack_optimus_core_v1beta1_runtime_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_raystack_optimus_core_v1beta1_runtime_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*VersionRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_runtime_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*VersionResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_raystack_optimus_core_v1beta1_runtime_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_raystack_optimus_core_v1beta1_runtime_proto_goTypes, + DependencyIndexes: file_raystack_optimus_core_v1beta1_runtime_proto_depIdxs, + MessageInfos: file_raystack_optimus_core_v1beta1_runtime_proto_msgTypes, + }.Build() + File_raystack_optimus_core_v1beta1_runtime_proto = out.File + file_raystack_optimus_core_v1beta1_runtime_proto_rawDesc = nil + file_raystack_optimus_core_v1beta1_runtime_proto_goTypes = nil + file_raystack_optimus_core_v1beta1_runtime_proto_depIdxs = nil +} diff --git a/protos/odpf/optimus/core/v1beta1/runtime.pb.gw.go b/protos/raystack/optimus/core/v1beta1/runtime.pb.gw.go similarity index 95% rename from protos/odpf/optimus/core/v1beta1/runtime.pb.gw.go rename to protos/raystack/optimus/core/v1beta1/runtime.pb.gw.go index 95da6a84bb..baa1d78acf 100644 --- a/protos/odpf/optimus/core/v1beta1/runtime.pb.gw.go +++ b/protos/raystack/optimus/core/v1beta1/runtime.pb.gw.go @@ -1,5 +1,5 @@ // Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. -// source: odpf/optimus/core/v1beta1/runtime.proto +// source: raystack/optimus/core/v1beta1/runtime.proto /* Package optimus is a reverse proxy. @@ -77,7 +77,7 @@ func RegisterRuntimeServiceHandlerServer(ctx context.Context, mux *runtime.Serve var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.RuntimeService/Version", runtime.WithHTTPPathPattern("/v1beta1/version")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.RuntimeService/Version", runtime.WithHTTPPathPattern("/v1beta1/version")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -139,7 +139,7 @@ func RegisterRuntimeServiceHandlerClient(ctx context.Context, mux *runtime.Serve ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.RuntimeService/Version", runtime.WithHTTPPathPattern("/v1beta1/version")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.RuntimeService/Version", runtime.WithHTTPPathPattern("/v1beta1/version")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return diff --git a/protos/odpf/optimus/core/v1beta1/runtime.swagger.json b/protos/raystack/optimus/core/v1beta1/runtime.swagger.json similarity index 89% rename from protos/odpf/optimus/core/v1beta1/runtime.swagger.json rename to protos/raystack/optimus/core/v1beta1/runtime.swagger.json index eedac90626..63de81808f 100644 --- a/protos/odpf/optimus/core/v1beta1/runtime.swagger.json +++ b/protos/raystack/optimus/core/v1beta1/runtime.swagger.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "title": "odpf/optimus/core/v1beta1/runtime.proto", + "title": "raystack/optimus/core/v1beta1/runtime.proto", "version": "0.1" }, "tags": [ @@ -11,15 +11,9 @@ ], "host": "127.0.0.1:9100", "basePath": "/api", - "schemes": [ - "http" - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], + "schemes": ["http"], + "consumes": ["application/json"], + "produces": ["application/json"], "paths": { "/v1beta1/version": { "post": { @@ -49,9 +43,7 @@ } } ], - "tags": [ - "RuntimeService" - ] + "tags": ["RuntimeService"] } } }, diff --git a/protos/odpf/optimus/core/v1beta1/runtime_grpc.pb.go b/protos/raystack/optimus/core/v1beta1/runtime_grpc.pb.go similarity index 91% rename from protos/odpf/optimus/core/v1beta1/runtime_grpc.pb.go rename to protos/raystack/optimus/core/v1beta1/runtime_grpc.pb.go index cad51fe26c..29947f2994 100644 --- a/protos/odpf/optimus/core/v1beta1/runtime_grpc.pb.go +++ b/protos/raystack/optimus/core/v1beta1/runtime_grpc.pb.go @@ -2,7 +2,7 @@ // versions: // - protoc-gen-go-grpc v1.2.0 // - protoc (unknown) -// source: odpf/optimus/core/v1beta1/runtime.proto +// source: raystack/optimus/core/v1beta1/runtime.proto package optimus @@ -36,7 +36,7 @@ func NewRuntimeServiceClient(cc grpc.ClientConnInterface) RuntimeServiceClient { func (c *runtimeServiceClient) Version(ctx context.Context, in *VersionRequest, opts ...grpc.CallOption) (*VersionResponse, error) { out := new(VersionResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.RuntimeService/Version", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.RuntimeService/Version", in, out, opts...) if err != nil { return nil, err } @@ -82,7 +82,7 @@ func _RuntimeService_Version_Handler(srv interface{}, ctx context.Context, dec f } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.RuntimeService/Version", + FullMethod: "/raystack.optimus.core.v1beta1.RuntimeService/Version", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(RuntimeServiceServer).Version(ctx, req.(*VersionRequest)) @@ -94,7 +94,7 @@ func _RuntimeService_Version_Handler(srv interface{}, ctx context.Context, dec f // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var RuntimeService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "odpf.optimus.core.v1beta1.RuntimeService", + ServiceName: "raystack.optimus.core.v1beta1.RuntimeService", HandlerType: (*RuntimeServiceServer)(nil), Methods: []grpc.MethodDesc{ { @@ -103,5 +103,5 @@ var RuntimeService_ServiceDesc = grpc.ServiceDesc{ }, }, Streams: []grpc.StreamDesc{}, - Metadata: "odpf/optimus/core/v1beta1/runtime.proto", + Metadata: "raystack/optimus/core/v1beta1/runtime.proto", } diff --git a/protos/raystack/optimus/core/v1beta1/secret.pb.go b/protos/raystack/optimus/core/v1beta1/secret.pb.go new file mode 100644 index 0000000000..4acc6b8f5a --- /dev/null +++ b/protos/raystack/optimus/core/v1beta1/secret.pb.go @@ -0,0 +1,814 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.0 +// protoc (unknown) +// source: raystack/optimus/core/v1beta1/secret.proto + +package optimus + +import ( + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type RegisterSecretRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + SecretName string `protobuf:"bytes,2,opt,name=secret_name,json=secretName,proto3" json:"secret_name,omitempty"` + Value string `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` // base64 encoded secret value + NamespaceName string `protobuf:"bytes,4,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` +} + +func (x *RegisterSecretRequest) Reset() { + *x = RegisterSecretRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisterSecretRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterSecretRequest) ProtoMessage() {} + +func (x *RegisterSecretRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterSecretRequest.ProtoReflect.Descriptor instead. +func (*RegisterSecretRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_secret_proto_rawDescGZIP(), []int{0} +} + +func (x *RegisterSecretRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *RegisterSecretRequest) GetSecretName() string { + if x != nil { + return x.SecretName + } + return "" +} + +func (x *RegisterSecretRequest) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +func (x *RegisterSecretRequest) GetNamespaceName() string { + if x != nil { + return x.NamespaceName + } + return "" +} + +type RegisterSecretResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *RegisterSecretResponse) Reset() { + *x = RegisterSecretResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisterSecretResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterSecretResponse) ProtoMessage() {} + +func (x *RegisterSecretResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterSecretResponse.ProtoReflect.Descriptor instead. +func (*RegisterSecretResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_secret_proto_rawDescGZIP(), []int{1} +} + +type UpdateSecretRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + SecretName string `protobuf:"bytes,2,opt,name=secret_name,json=secretName,proto3" json:"secret_name,omitempty"` + Value string `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` // base64 encoded secret value + NamespaceName string `protobuf:"bytes,4,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` +} + +func (x *UpdateSecretRequest) Reset() { + *x = UpdateSecretRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateSecretRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateSecretRequest) ProtoMessage() {} + +func (x *UpdateSecretRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateSecretRequest.ProtoReflect.Descriptor instead. +func (*UpdateSecretRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_secret_proto_rawDescGZIP(), []int{2} +} + +func (x *UpdateSecretRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *UpdateSecretRequest) GetSecretName() string { + if x != nil { + return x.SecretName + } + return "" +} + +func (x *UpdateSecretRequest) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +func (x *UpdateSecretRequest) GetNamespaceName() string { + if x != nil { + return x.NamespaceName + } + return "" +} + +type UpdateSecretResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *UpdateSecretResponse) Reset() { + *x = UpdateSecretResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateSecretResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateSecretResponse) ProtoMessage() {} + +func (x *UpdateSecretResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateSecretResponse.ProtoReflect.Descriptor instead. +func (*UpdateSecretResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_secret_proto_rawDescGZIP(), []int{3} +} + +type ListSecretsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` +} + +func (x *ListSecretsRequest) Reset() { + *x = ListSecretsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListSecretsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListSecretsRequest) ProtoMessage() {} + +func (x *ListSecretsRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListSecretsRequest.ProtoReflect.Descriptor instead. +func (*ListSecretsRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_secret_proto_rawDescGZIP(), []int{4} +} + +func (x *ListSecretsRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +type ListSecretsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Secrets []*ListSecretsResponse_Secret `protobuf:"bytes,1,rep,name=secrets,proto3" json:"secrets,omitempty"` +} + +func (x *ListSecretsResponse) Reset() { + *x = ListSecretsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListSecretsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListSecretsResponse) ProtoMessage() {} + +func (x *ListSecretsResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListSecretsResponse.ProtoReflect.Descriptor instead. +func (*ListSecretsResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_secret_proto_rawDescGZIP(), []int{5} +} + +func (x *ListSecretsResponse) GetSecrets() []*ListSecretsResponse_Secret { + if x != nil { + return x.Secrets + } + return nil +} + +type DeleteSecretRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + SecretName string `protobuf:"bytes,2,opt,name=secret_name,json=secretName,proto3" json:"secret_name,omitempty"` + NamespaceName string `protobuf:"bytes,3,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` +} + +func (x *DeleteSecretRequest) Reset() { + *x = DeleteSecretRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteSecretRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteSecretRequest) ProtoMessage() {} + +func (x *DeleteSecretRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteSecretRequest.ProtoReflect.Descriptor instead. +func (*DeleteSecretRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_secret_proto_rawDescGZIP(), []int{6} +} + +func (x *DeleteSecretRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *DeleteSecretRequest) GetSecretName() string { + if x != nil { + return x.SecretName + } + return "" +} + +func (x *DeleteSecretRequest) GetNamespaceName() string { + if x != nil { + return x.NamespaceName + } + return "" +} + +type DeleteSecretResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *DeleteSecretResponse) Reset() { + *x = DeleteSecretResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteSecretResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteSecretResponse) ProtoMessage() {} + +func (x *DeleteSecretResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteSecretResponse.ProtoReflect.Descriptor instead. +func (*DeleteSecretResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_secret_proto_rawDescGZIP(), []int{7} +} + +type ListSecretsResponse_Secret struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Digest string `protobuf:"bytes,2,opt,name=digest,proto3" json:"digest,omitempty"` + Namespace string `protobuf:"bytes,3,opt,name=namespace,proto3" json:"namespace,omitempty"` + UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` +} + +func (x *ListSecretsResponse_Secret) Reset() { + *x = ListSecretsResponse_Secret{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListSecretsResponse_Secret) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListSecretsResponse_Secret) ProtoMessage() {} + +func (x *ListSecretsResponse_Secret) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListSecretsResponse_Secret.ProtoReflect.Descriptor instead. +func (*ListSecretsResponse_Secret) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_secret_proto_rawDescGZIP(), []int{5, 0} +} + +func (x *ListSecretsResponse_Secret) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ListSecretsResponse_Secret) GetDigest() string { + if x != nil { + return x.Digest + } + return "" +} + +func (x *ListSecretsResponse_Secret) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *ListSecretsResponse_Secret) GetUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedAt + } + return nil +} + +var File_raystack_optimus_core_v1beta1_secret_proto protoreflect.FileDescriptor + +var file_raystack_optimus_core_v1beta1_secret_proto_rawDesc = []byte{ + 0x0a, 0x2d, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2f, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x20, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, + 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, + 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, + 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x22, 0x98, 0x01, 0x0a, 0x15, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x53, 0x65, 0x63, + 0x72, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, + 0x0b, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x18, 0x0a, 0x16, 0x52, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x96, 0x01, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, + 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x16, + 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x37, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, + 0x63, 0x72, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, + 0xfd, 0x01, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, + 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x1a, + 0x8d, 0x01, 0x0a, 0x06, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, + 0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, + 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, + 0x80, 0x01, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, + 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x22, 0x16, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x82, 0x06, 0x0a, 0x0d, 0x53, + 0x65, 0x63, 0x72, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xc4, 0x01, 0x0a, + 0x0e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, + 0x37, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x53, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x65, 0x72, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x3f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x39, 0x22, 0x34, 0x2f, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x73, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x2f, 0x7b, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, + 0x3a, 0x01, 0x2a, 0x12, 0xbe, 0x01, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, + 0x63, 0x72, 0x65, 0x74, 0x12, 0x35, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, + 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x67, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, + 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x3f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x39, 0x1a, 0x34, 0x2f, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x73, 0x65, 0x63, + 0x72, 0x65, 0x74, 0x2f, 0x7b, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x7d, 0x3a, 0x01, 0x2a, 0x12, 0xaa, 0x01, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x63, + 0x72, 0x65, 0x74, 0x73, 0x12, 0x34, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x67, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, + 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x12, 0x26, 0x2f, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x73, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x12, 0xbb, 0x01, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x12, 0x35, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x3c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x36, 0x2a, 0x34, 0x2f, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x7b, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x73, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x2f, 0x7b, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x42, + 0xa0, 0x01, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x75, 0x73, 0x42, 0x14, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x1e, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x6e, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x92, 0x41, 0x45, 0x12, 0x05, 0x32, + 0x03, 0x30, 0x2e, 0x31, 0x1a, 0x0e, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x3a, + 0x39, 0x31, 0x30, 0x30, 0x22, 0x04, 0x2f, 0x61, 0x70, 0x69, 0x2a, 0x01, 0x01, 0x72, 0x23, 0x0a, + 0x21, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x20, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x20, + 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_raystack_optimus_core_v1beta1_secret_proto_rawDescOnce sync.Once + file_raystack_optimus_core_v1beta1_secret_proto_rawDescData = file_raystack_optimus_core_v1beta1_secret_proto_rawDesc +) + +func file_raystack_optimus_core_v1beta1_secret_proto_rawDescGZIP() []byte { + file_raystack_optimus_core_v1beta1_secret_proto_rawDescOnce.Do(func() { + file_raystack_optimus_core_v1beta1_secret_proto_rawDescData = protoimpl.X.CompressGZIP(file_raystack_optimus_core_v1beta1_secret_proto_rawDescData) + }) + return file_raystack_optimus_core_v1beta1_secret_proto_rawDescData +} + +var file_raystack_optimus_core_v1beta1_secret_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_raystack_optimus_core_v1beta1_secret_proto_goTypes = []interface{}{ + (*RegisterSecretRequest)(nil), // 0: raystack.optimus.core.v1beta1.RegisterSecretRequest + (*RegisterSecretResponse)(nil), // 1: raystack.optimus.core.v1beta1.RegisterSecretResponse + (*UpdateSecretRequest)(nil), // 2: raystack.optimus.core.v1beta1.UpdateSecretRequest + (*UpdateSecretResponse)(nil), // 3: raystack.optimus.core.v1beta1.UpdateSecretResponse + (*ListSecretsRequest)(nil), // 4: raystack.optimus.core.v1beta1.ListSecretsRequest + (*ListSecretsResponse)(nil), // 5: raystack.optimus.core.v1beta1.ListSecretsResponse + (*DeleteSecretRequest)(nil), // 6: raystack.optimus.core.v1beta1.DeleteSecretRequest + (*DeleteSecretResponse)(nil), // 7: raystack.optimus.core.v1beta1.DeleteSecretResponse + (*ListSecretsResponse_Secret)(nil), // 8: raystack.optimus.core.v1beta1.ListSecretsResponse.Secret + (*timestamppb.Timestamp)(nil), // 9: google.protobuf.Timestamp +} +var file_raystack_optimus_core_v1beta1_secret_proto_depIdxs = []int32{ + 8, // 0: raystack.optimus.core.v1beta1.ListSecretsResponse.secrets:type_name -> raystack.optimus.core.v1beta1.ListSecretsResponse.Secret + 9, // 1: raystack.optimus.core.v1beta1.ListSecretsResponse.Secret.updated_at:type_name -> google.protobuf.Timestamp + 0, // 2: raystack.optimus.core.v1beta1.SecretService.RegisterSecret:input_type -> raystack.optimus.core.v1beta1.RegisterSecretRequest + 2, // 3: raystack.optimus.core.v1beta1.SecretService.UpdateSecret:input_type -> raystack.optimus.core.v1beta1.UpdateSecretRequest + 4, // 4: raystack.optimus.core.v1beta1.SecretService.ListSecrets:input_type -> raystack.optimus.core.v1beta1.ListSecretsRequest + 6, // 5: raystack.optimus.core.v1beta1.SecretService.DeleteSecret:input_type -> raystack.optimus.core.v1beta1.DeleteSecretRequest + 1, // 6: raystack.optimus.core.v1beta1.SecretService.RegisterSecret:output_type -> raystack.optimus.core.v1beta1.RegisterSecretResponse + 3, // 7: raystack.optimus.core.v1beta1.SecretService.UpdateSecret:output_type -> raystack.optimus.core.v1beta1.UpdateSecretResponse + 5, // 8: raystack.optimus.core.v1beta1.SecretService.ListSecrets:output_type -> raystack.optimus.core.v1beta1.ListSecretsResponse + 7, // 9: raystack.optimus.core.v1beta1.SecretService.DeleteSecret:output_type -> raystack.optimus.core.v1beta1.DeleteSecretResponse + 6, // [6:10] is the sub-list for method output_type + 2, // [2:6] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_raystack_optimus_core_v1beta1_secret_proto_init() } +func file_raystack_optimus_core_v1beta1_secret_proto_init() { + if File_raystack_optimus_core_v1beta1_secret_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterSecretRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterSecretResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateSecretRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateSecretResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListSecretsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListSecretsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteSecretRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteSecretResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_core_v1beta1_secret_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListSecretsResponse_Secret); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_raystack_optimus_core_v1beta1_secret_proto_rawDesc, + NumEnums: 0, + NumMessages: 9, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_raystack_optimus_core_v1beta1_secret_proto_goTypes, + DependencyIndexes: file_raystack_optimus_core_v1beta1_secret_proto_depIdxs, + MessageInfos: file_raystack_optimus_core_v1beta1_secret_proto_msgTypes, + }.Build() + File_raystack_optimus_core_v1beta1_secret_proto = out.File + file_raystack_optimus_core_v1beta1_secret_proto_rawDesc = nil + file_raystack_optimus_core_v1beta1_secret_proto_goTypes = nil + file_raystack_optimus_core_v1beta1_secret_proto_depIdxs = nil +} diff --git a/protos/odpf/optimus/core/v1beta1/secret.pb.gw.go b/protos/raystack/optimus/core/v1beta1/secret.pb.gw.go similarity index 94% rename from protos/odpf/optimus/core/v1beta1/secret.pb.gw.go rename to protos/raystack/optimus/core/v1beta1/secret.pb.gw.go index 3f7bb3cf09..59163cb9d1 100644 --- a/protos/odpf/optimus/core/v1beta1/secret.pb.gw.go +++ b/protos/raystack/optimus/core/v1beta1/secret.pb.gw.go @@ -1,5 +1,5 @@ // Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. -// source: odpf/optimus/core/v1beta1/secret.proto +// source: raystack/optimus/core/v1beta1/secret.proto /* Package optimus is a reverse proxy. @@ -361,7 +361,7 @@ func RegisterSecretServiceHandlerServer(ctx context.Context, mux *runtime.ServeM var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.SecretService/RegisterSecret", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/secret/{secret_name}")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.SecretService/RegisterSecret", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/secret/{secret_name}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -384,7 +384,7 @@ func RegisterSecretServiceHandlerServer(ctx context.Context, mux *runtime.ServeM var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.SecretService/UpdateSecret", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/secret/{secret_name}")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.SecretService/UpdateSecret", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/secret/{secret_name}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -407,7 +407,7 @@ func RegisterSecretServiceHandlerServer(ctx context.Context, mux *runtime.ServeM var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.SecretService/ListSecrets", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/secret")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.SecretService/ListSecrets", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/secret")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -430,7 +430,7 @@ func RegisterSecretServiceHandlerServer(ctx context.Context, mux *runtime.ServeM var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.SecretService/DeleteSecret", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/secret/{secret_name}")) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.SecretService/DeleteSecret", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/secret/{secret_name}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -492,7 +492,7 @@ func RegisterSecretServiceHandlerClient(ctx context.Context, mux *runtime.ServeM ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.SecretService/RegisterSecret", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/secret/{secret_name}")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.SecretService/RegisterSecret", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/secret/{secret_name}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -512,7 +512,7 @@ func RegisterSecretServiceHandlerClient(ctx context.Context, mux *runtime.ServeM ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.SecretService/UpdateSecret", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/secret/{secret_name}")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.SecretService/UpdateSecret", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/secret/{secret_name}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -532,7 +532,7 @@ func RegisterSecretServiceHandlerClient(ctx context.Context, mux *runtime.ServeM ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.SecretService/ListSecrets", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/secret")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.SecretService/ListSecrets", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/secret")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -552,7 +552,7 @@ func RegisterSecretServiceHandlerClient(ctx context.Context, mux *runtime.ServeM ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/odpf.optimus.core.v1beta1.SecretService/DeleteSecret", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/secret/{secret_name}")) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/raystack.optimus.core.v1beta1.SecretService/DeleteSecret", runtime.WithHTTPPathPattern("/v1beta1/project/{project_name}/secret/{secret_name}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return diff --git a/protos/odpf/optimus/core/v1beta1/secret.swagger.json b/protos/raystack/optimus/core/v1beta1/secret.swagger.json similarity index 94% rename from protos/odpf/optimus/core/v1beta1/secret.swagger.json rename to protos/raystack/optimus/core/v1beta1/secret.swagger.json index 8a1c62ab39..47296d5176 100644 --- a/protos/odpf/optimus/core/v1beta1/secret.swagger.json +++ b/protos/raystack/optimus/core/v1beta1/secret.swagger.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "title": "odpf/optimus/core/v1beta1/secret.proto", + "title": "raystack/optimus/core/v1beta1/secret.proto", "version": "0.1" }, "tags": [ @@ -11,15 +11,9 @@ ], "host": "127.0.0.1:9100", "basePath": "/api", - "schemes": [ - "http" - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], + "schemes": ["http"], + "consumes": ["application/json"], + "produces": ["application/json"], "paths": { "/v1beta1/project/{projectName}/secret": { "get": { @@ -47,9 +41,7 @@ "type": "string" } ], - "tags": [ - "SecretService" - ] + "tags": ["SecretService"] } }, "/v1beta1/project/{projectName}/secret/{secretName}": { @@ -90,9 +82,7 @@ "type": "string" } ], - "tags": [ - "SecretService" - ] + "tags": ["SecretService"] }, "post": { "summary": "RegisterSecret creates a new secret of a project", @@ -141,9 +131,7 @@ } } ], - "tags": [ - "SecretService" - ] + "tags": ["SecretService"] }, "put": { "summary": "UpdateSecret updates secret at project level", @@ -192,9 +180,7 @@ } } ], - "tags": [ - "SecretService" - ] + "tags": ["SecretService"] } } }, diff --git a/protos/odpf/optimus/core/v1beta1/secret_grpc.pb.go b/protos/raystack/optimus/core/v1beta1/secret_grpc.pb.go similarity index 90% rename from protos/odpf/optimus/core/v1beta1/secret_grpc.pb.go rename to protos/raystack/optimus/core/v1beta1/secret_grpc.pb.go index 2e202efce9..044cad59d1 100644 --- a/protos/odpf/optimus/core/v1beta1/secret_grpc.pb.go +++ b/protos/raystack/optimus/core/v1beta1/secret_grpc.pb.go @@ -2,7 +2,7 @@ // versions: // - protoc-gen-go-grpc v1.2.0 // - protoc (unknown) -// source: odpf/optimus/core/v1beta1/secret.proto +// source: raystack/optimus/core/v1beta1/secret.proto package optimus @@ -42,7 +42,7 @@ func NewSecretServiceClient(cc grpc.ClientConnInterface) SecretServiceClient { func (c *secretServiceClient) RegisterSecret(ctx context.Context, in *RegisterSecretRequest, opts ...grpc.CallOption) (*RegisterSecretResponse, error) { out := new(RegisterSecretResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.SecretService/RegisterSecret", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.SecretService/RegisterSecret", in, out, opts...) if err != nil { return nil, err } @@ -51,7 +51,7 @@ func (c *secretServiceClient) RegisterSecret(ctx context.Context, in *RegisterSe func (c *secretServiceClient) UpdateSecret(ctx context.Context, in *UpdateSecretRequest, opts ...grpc.CallOption) (*UpdateSecretResponse, error) { out := new(UpdateSecretResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.SecretService/UpdateSecret", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.SecretService/UpdateSecret", in, out, opts...) if err != nil { return nil, err } @@ -60,7 +60,7 @@ func (c *secretServiceClient) UpdateSecret(ctx context.Context, in *UpdateSecret func (c *secretServiceClient) ListSecrets(ctx context.Context, in *ListSecretsRequest, opts ...grpc.CallOption) (*ListSecretsResponse, error) { out := new(ListSecretsResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.SecretService/ListSecrets", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.SecretService/ListSecrets", in, out, opts...) if err != nil { return nil, err } @@ -69,7 +69,7 @@ func (c *secretServiceClient) ListSecrets(ctx context.Context, in *ListSecretsRe func (c *secretServiceClient) DeleteSecret(ctx context.Context, in *DeleteSecretRequest, opts ...grpc.CallOption) (*DeleteSecretResponse, error) { out := new(DeleteSecretResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.core.v1beta1.SecretService/DeleteSecret", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.core.v1beta1.SecretService/DeleteSecret", in, out, opts...) if err != nil { return nil, err } @@ -130,7 +130,7 @@ func _SecretService_RegisterSecret_Handler(srv interface{}, ctx context.Context, } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.SecretService/RegisterSecret", + FullMethod: "/raystack.optimus.core.v1beta1.SecretService/RegisterSecret", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(SecretServiceServer).RegisterSecret(ctx, req.(*RegisterSecretRequest)) @@ -148,7 +148,7 @@ func _SecretService_UpdateSecret_Handler(srv interface{}, ctx context.Context, d } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.SecretService/UpdateSecret", + FullMethod: "/raystack.optimus.core.v1beta1.SecretService/UpdateSecret", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(SecretServiceServer).UpdateSecret(ctx, req.(*UpdateSecretRequest)) @@ -166,7 +166,7 @@ func _SecretService_ListSecrets_Handler(srv interface{}, ctx context.Context, de } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.SecretService/ListSecrets", + FullMethod: "/raystack.optimus.core.v1beta1.SecretService/ListSecrets", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(SecretServiceServer).ListSecrets(ctx, req.(*ListSecretsRequest)) @@ -184,7 +184,7 @@ func _SecretService_DeleteSecret_Handler(srv interface{}, ctx context.Context, d } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.core.v1beta1.SecretService/DeleteSecret", + FullMethod: "/raystack.optimus.core.v1beta1.SecretService/DeleteSecret", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(SecretServiceServer).DeleteSecret(ctx, req.(*DeleteSecretRequest)) @@ -196,7 +196,7 @@ func _SecretService_DeleteSecret_Handler(srv interface{}, ctx context.Context, d // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var SecretService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "odpf.optimus.core.v1beta1.SecretService", + ServiceName: "raystack.optimus.core.v1beta1.SecretService", HandlerType: (*SecretServiceServer)(nil), Methods: []grpc.MethodDesc{ { @@ -217,5 +217,5 @@ var SecretService_ServiceDesc = grpc.ServiceDesc{ }, }, Streams: []grpc.StreamDesc{}, - Metadata: "odpf/optimus/core/v1beta1/secret.proto", + Metadata: "raystack/optimus/core/v1beta1/secret.proto", } diff --git a/protos/raystack/optimus/core/v1beta1/status.pb.go b/protos/raystack/optimus/core/v1beta1/status.pb.go new file mode 100644 index 0000000000..8f5c185804 --- /dev/null +++ b/protos/raystack/optimus/core/v1beta1/status.pb.go @@ -0,0 +1,234 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.0 +// protoc (unknown) +// source: raystack/optimus/core/v1beta1/status.proto + +package optimus + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Level int32 + +const ( + Level_LEVEL_UNSPECIFIED Level = 0 + Level_LEVEL_TRACE Level = 1 + Level_LEVEL_DEBUG Level = 2 + Level_LEVEL_INFO Level = 3 + Level_LEVEL_WARNING Level = 4 + Level_LEVEL_ERROR Level = 5 + Level_LEVEL_FATAL Level = 6 +) + +// Enum value maps for Level. +var ( + Level_name = map[int32]string{ + 0: "LEVEL_UNSPECIFIED", + 1: "LEVEL_TRACE", + 2: "LEVEL_DEBUG", + 3: "LEVEL_INFO", + 4: "LEVEL_WARNING", + 5: "LEVEL_ERROR", + 6: "LEVEL_FATAL", + } + Level_value = map[string]int32{ + "LEVEL_UNSPECIFIED": 0, + "LEVEL_TRACE": 1, + "LEVEL_DEBUG": 2, + "LEVEL_INFO": 3, + "LEVEL_WARNING": 4, + "LEVEL_ERROR": 5, + "LEVEL_FATAL": 6, + } +) + +func (x Level) Enum() *Level { + p := new(Level) + *p = x + return p +} + +func (x Level) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Level) Descriptor() protoreflect.EnumDescriptor { + return file_raystack_optimus_core_v1beta1_status_proto_enumTypes[0].Descriptor() +} + +func (Level) Type() protoreflect.EnumType { + return &file_raystack_optimus_core_v1beta1_status_proto_enumTypes[0] +} + +func (x Level) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Level.Descriptor instead. +func (Level) EnumDescriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_status_proto_rawDescGZIP(), []int{0} +} + +type Log struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Level Level `protobuf:"varint,1,opt,name=level,proto3,enum=raystack.optimus.core.v1beta1.Level" json:"level,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *Log) Reset() { + *x = Log{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_core_v1beta1_status_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Log) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Log) ProtoMessage() {} + +func (x *Log) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_core_v1beta1_status_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Log.ProtoReflect.Descriptor instead. +func (*Log) Descriptor() ([]byte, []int) { + return file_raystack_optimus_core_v1beta1_status_proto_rawDescGZIP(), []int{0} +} + +func (x *Log) GetLevel() Level { + if x != nil { + return x.Level + } + return Level_LEVEL_UNSPECIFIED +} + +func (x *Log) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +var File_raystack_optimus_core_v1beta1_status_proto protoreflect.FileDescriptor + +var file_raystack_optimus_core_v1beta1_status_proto_rawDesc = []byte{ + 0x0a, 0x2d, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2f, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x20, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x22, 0x5e, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x3d, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, + 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x65, 0x76, 0x65, 0x6c, + 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x2a, 0x85, 0x01, 0x0a, 0x05, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x15, 0x0a, 0x11, 0x4c, + 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, + 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x54, 0x52, 0x41, 0x43, + 0x45, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x44, 0x45, 0x42, + 0x55, 0x47, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x49, 0x4e, + 0x46, 0x4f, 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x57, 0x41, + 0x52, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x04, 0x12, 0x0f, 0x0a, 0x0b, 0x4c, 0x45, 0x56, 0x45, 0x4c, + 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x05, 0x12, 0x0f, 0x0a, 0x0b, 0x4c, 0x45, 0x56, 0x45, + 0x4c, 0x5f, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x10, 0x06, 0x42, 0x4a, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, + 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x6e, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x42, 0x06, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x50, 0x01, 0x5a, 0x1e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x67, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_raystack_optimus_core_v1beta1_status_proto_rawDescOnce sync.Once + file_raystack_optimus_core_v1beta1_status_proto_rawDescData = file_raystack_optimus_core_v1beta1_status_proto_rawDesc +) + +func file_raystack_optimus_core_v1beta1_status_proto_rawDescGZIP() []byte { + file_raystack_optimus_core_v1beta1_status_proto_rawDescOnce.Do(func() { + file_raystack_optimus_core_v1beta1_status_proto_rawDescData = protoimpl.X.CompressGZIP(file_raystack_optimus_core_v1beta1_status_proto_rawDescData) + }) + return file_raystack_optimus_core_v1beta1_status_proto_rawDescData +} + +var file_raystack_optimus_core_v1beta1_status_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_raystack_optimus_core_v1beta1_status_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_raystack_optimus_core_v1beta1_status_proto_goTypes = []interface{}{ + (Level)(0), // 0: raystack.optimus.core.v1beta1.Level + (*Log)(nil), // 1: raystack.optimus.core.v1beta1.Log +} +var file_raystack_optimus_core_v1beta1_status_proto_depIdxs = []int32{ + 0, // 0: raystack.optimus.core.v1beta1.Log.level:type_name -> raystack.optimus.core.v1beta1.Level + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_raystack_optimus_core_v1beta1_status_proto_init() } +func file_raystack_optimus_core_v1beta1_status_proto_init() { + if File_raystack_optimus_core_v1beta1_status_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_raystack_optimus_core_v1beta1_status_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Log); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_raystack_optimus_core_v1beta1_status_proto_rawDesc, + NumEnums: 1, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_raystack_optimus_core_v1beta1_status_proto_goTypes, + DependencyIndexes: file_raystack_optimus_core_v1beta1_status_proto_depIdxs, + EnumInfos: file_raystack_optimus_core_v1beta1_status_proto_enumTypes, + MessageInfos: file_raystack_optimus_core_v1beta1_status_proto_msgTypes, + }.Build() + File_raystack_optimus_core_v1beta1_status_proto = out.File + file_raystack_optimus_core_v1beta1_status_proto_rawDesc = nil + file_raystack_optimus_core_v1beta1_status_proto_goTypes = nil + file_raystack_optimus_core_v1beta1_status_proto_depIdxs = nil +} diff --git a/protos/odpf/optimus/core/v1beta1/status.swagger.json b/protos/raystack/optimus/core/v1beta1/status.swagger.json similarity index 83% rename from protos/odpf/optimus/core/v1beta1/status.swagger.json rename to protos/raystack/optimus/core/v1beta1/status.swagger.json index f4eb363d8d..b301d14969 100644 --- a/protos/odpf/optimus/core/v1beta1/status.swagger.json +++ b/protos/raystack/optimus/core/v1beta1/status.swagger.json @@ -1,15 +1,11 @@ { "swagger": "2.0", "info": { - "title": "odpf/optimus/core/v1beta1/status.proto", + "title": "raystack/optimus/core/v1beta1/status.proto", "version": "version not set" }, - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], + "consumes": ["application/json"], + "produces": ["application/json"], "paths": {}, "definitions": { "protobufAny": { diff --git a/protos/raystack/optimus/integration/v1beta1/event.pb.go b/protos/raystack/optimus/integration/v1beta1/event.pb.go new file mode 100644 index 0000000000..dadc8a1c1c --- /dev/null +++ b/protos/raystack/optimus/integration/v1beta1/event.pb.go @@ -0,0 +1,746 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.0 +// protoc (unknown) +// source: raystack/optimus/integration/v1beta1/event.proto + +package optimus + +import ( + v1beta1 "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type OptimusChangeEvent_EventType int32 + +const ( + OptimusChangeEvent_EVENT_TYPE_TYPE_UNSPECIFIED OptimusChangeEvent_EventType = 0 + OptimusChangeEvent_EVENT_TYPE_RESOURCE_CREATE OptimusChangeEvent_EventType = 1 + OptimusChangeEvent_EVENT_TYPE_RESOURCE_UPDATE OptimusChangeEvent_EventType = 2 + OptimusChangeEvent_EVENT_TYPE_JOB_CREATE OptimusChangeEvent_EventType = 3 + OptimusChangeEvent_EVENT_TYPE_JOB_UPDATE OptimusChangeEvent_EventType = 4 + OptimusChangeEvent_EVENT_TYPE_JOB_DELETE OptimusChangeEvent_EventType = 5 + OptimusChangeEvent_EVENT_TYPE_JOB_WAIT_UPSTREAM OptimusChangeEvent_EventType = 6 + OptimusChangeEvent_EVENT_TYPE_JOB_IN_PROGRESS OptimusChangeEvent_EventType = 7 + OptimusChangeEvent_EVENT_TYPE_JOB_SUCCESS OptimusChangeEvent_EventType = 8 + OptimusChangeEvent_EVENT_TYPE_JOB_FAILURE OptimusChangeEvent_EventType = 9 + OptimusChangeEvent_EVENT_TYPE_JOB_STATE_CHANGE OptimusChangeEvent_EventType = 10 +) + +// Enum value maps for OptimusChangeEvent_EventType. +var ( + OptimusChangeEvent_EventType_name = map[int32]string{ + 0: "EVENT_TYPE_TYPE_UNSPECIFIED", + 1: "EVENT_TYPE_RESOURCE_CREATE", + 2: "EVENT_TYPE_RESOURCE_UPDATE", + 3: "EVENT_TYPE_JOB_CREATE", + 4: "EVENT_TYPE_JOB_UPDATE", + 5: "EVENT_TYPE_JOB_DELETE", + 6: "EVENT_TYPE_JOB_WAIT_UPSTREAM", + 7: "EVENT_TYPE_JOB_IN_PROGRESS", + 8: "EVENT_TYPE_JOB_SUCCESS", + 9: "EVENT_TYPE_JOB_FAILURE", + 10: "EVENT_TYPE_JOB_STATE_CHANGE", + } + OptimusChangeEvent_EventType_value = map[string]int32{ + "EVENT_TYPE_TYPE_UNSPECIFIED": 0, + "EVENT_TYPE_RESOURCE_CREATE": 1, + "EVENT_TYPE_RESOURCE_UPDATE": 2, + "EVENT_TYPE_JOB_CREATE": 3, + "EVENT_TYPE_JOB_UPDATE": 4, + "EVENT_TYPE_JOB_DELETE": 5, + "EVENT_TYPE_JOB_WAIT_UPSTREAM": 6, + "EVENT_TYPE_JOB_IN_PROGRESS": 7, + "EVENT_TYPE_JOB_SUCCESS": 8, + "EVENT_TYPE_JOB_FAILURE": 9, + "EVENT_TYPE_JOB_STATE_CHANGE": 10, + } +) + +func (x OptimusChangeEvent_EventType) Enum() *OptimusChangeEvent_EventType { + p := new(OptimusChangeEvent_EventType) + *p = x + return p +} + +func (x OptimusChangeEvent_EventType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (OptimusChangeEvent_EventType) Descriptor() protoreflect.EnumDescriptor { + return file_raystack_optimus_integration_v1beta1_event_proto_enumTypes[0].Descriptor() +} + +func (OptimusChangeEvent_EventType) Type() protoreflect.EnumType { + return &file_raystack_optimus_integration_v1beta1_event_proto_enumTypes[0] +} + +func (x OptimusChangeEvent_EventType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use OptimusChangeEvent_EventType.Descriptor instead. +func (OptimusChangeEvent_EventType) EnumDescriptor() ([]byte, []int) { + return file_raystack_optimus_integration_v1beta1_event_proto_rawDescGZIP(), []int{4, 0} +} + +type ResourceChangePayload struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + DatastoreName string `protobuf:"bytes,1,opt,name=datastore_name,json=datastoreName,proto3" json:"datastore_name,omitempty"` + Resource *v1beta1.ResourceSpecification `protobuf:"bytes,2,opt,name=resource,proto3" json:"resource,omitempty"` +} + +func (x *ResourceChangePayload) Reset() { + *x = ResourceChangePayload{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_integration_v1beta1_event_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ResourceChangePayload) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResourceChangePayload) ProtoMessage() {} + +func (x *ResourceChangePayload) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_integration_v1beta1_event_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ResourceChangePayload.ProtoReflect.Descriptor instead. +func (*ResourceChangePayload) Descriptor() ([]byte, []int) { + return file_raystack_optimus_integration_v1beta1_event_proto_rawDescGZIP(), []int{0} +} + +func (x *ResourceChangePayload) GetDatastoreName() string { + if x != nil { + return x.DatastoreName + } + return "" +} + +func (x *ResourceChangePayload) GetResource() *v1beta1.ResourceSpecification { + if x != nil { + return x.Resource + } + return nil +} + +type JobChangePayload struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + JobName string `protobuf:"bytes,1,opt,name=job_name,json=jobName,proto3" json:"job_name,omitempty"` + JobSpec *v1beta1.JobSpecification `protobuf:"bytes,2,opt,name=job_spec,json=jobSpec,proto3" json:"job_spec,omitempty"` +} + +func (x *JobChangePayload) Reset() { + *x = JobChangePayload{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_integration_v1beta1_event_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *JobChangePayload) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JobChangePayload) ProtoMessage() {} + +func (x *JobChangePayload) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_integration_v1beta1_event_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JobChangePayload.ProtoReflect.Descriptor instead. +func (*JobChangePayload) Descriptor() ([]byte, []int) { + return file_raystack_optimus_integration_v1beta1_event_proto_rawDescGZIP(), []int{1} +} + +func (x *JobChangePayload) GetJobName() string { + if x != nil { + return x.JobName + } + return "" +} + +func (x *JobChangePayload) GetJobSpec() *v1beta1.JobSpecification { + if x != nil { + return x.JobSpec + } + return nil +} + +type JobRunPayload struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + JobName string `protobuf:"bytes,1,opt,name=job_name,json=jobName,proto3" json:"job_name,omitempty"` + ScheduledAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=scheduled_at,json=scheduledAt,proto3" json:"scheduled_at,omitempty"` + JobRunId string `protobuf:"bytes,3,opt,name=job_run_id,json=jobRunId,proto3" json:"job_run_id,omitempty"` + StartTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` +} + +func (x *JobRunPayload) Reset() { + *x = JobRunPayload{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_integration_v1beta1_event_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *JobRunPayload) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JobRunPayload) ProtoMessage() {} + +func (x *JobRunPayload) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_integration_v1beta1_event_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JobRunPayload.ProtoReflect.Descriptor instead. +func (*JobRunPayload) Descriptor() ([]byte, []int) { + return file_raystack_optimus_integration_v1beta1_event_proto_rawDescGZIP(), []int{2} +} + +func (x *JobRunPayload) GetJobName() string { + if x != nil { + return x.JobName + } + return "" +} + +func (x *JobRunPayload) GetScheduledAt() *timestamppb.Timestamp { + if x != nil { + return x.ScheduledAt + } + return nil +} + +func (x *JobRunPayload) GetJobRunId() string { + if x != nil { + return x.JobRunId + } + return "" +} + +func (x *JobRunPayload) GetStartTime() *timestamppb.Timestamp { + if x != nil { + return x.StartTime + } + return nil +} + +type JobStateChangePayload struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + JobName string `protobuf:"bytes,1,opt,name=job_name,json=jobName,proto3" json:"job_name,omitempty"` + State v1beta1.JobState `protobuf:"varint,2,opt,name=state,proto3,enum=raystack.optimus.core.v1beta1.JobState" json:"state,omitempty"` +} + +func (x *JobStateChangePayload) Reset() { + *x = JobStateChangePayload{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_integration_v1beta1_event_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *JobStateChangePayload) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JobStateChangePayload) ProtoMessage() {} + +func (x *JobStateChangePayload) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_integration_v1beta1_event_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JobStateChangePayload.ProtoReflect.Descriptor instead. +func (*JobStateChangePayload) Descriptor() ([]byte, []int) { + return file_raystack_optimus_integration_v1beta1_event_proto_rawDescGZIP(), []int{3} +} + +func (x *JobStateChangePayload) GetJobName() string { + if x != nil { + return x.JobName + } + return "" +} + +func (x *JobStateChangePayload) GetState() v1beta1.JobState { + if x != nil { + return x.State + } + return v1beta1.JobState(0) +} + +type OptimusChangeEvent struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + EventId string `protobuf:"bytes,1,opt,name=event_id,json=eventId,proto3" json:"event_id,omitempty"` + OccurredAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=occurred_at,json=occurredAt,proto3" json:"occurred_at,omitempty"` + ProjectName string `protobuf:"bytes,3,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + NamespaceName string `protobuf:"bytes,4,opt,name=namespace_name,json=namespaceName,proto3" json:"namespace_name,omitempty"` + EventType OptimusChangeEvent_EventType `protobuf:"varint,5,opt,name=event_type,json=eventType,proto3,enum=raystack.optimus.integration.v1beta1.OptimusChangeEvent_EventType" json:"event_type,omitempty"` + // Types that are assignable to Payload: + // + // *OptimusChangeEvent_JobChange + // *OptimusChangeEvent_ResourceChange + // *OptimusChangeEvent_JobRun + // *OptimusChangeEvent_JobStateChange + Payload isOptimusChangeEvent_Payload `protobuf_oneof:"payload"` +} + +func (x *OptimusChangeEvent) Reset() { + *x = OptimusChangeEvent{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_integration_v1beta1_event_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OptimusChangeEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OptimusChangeEvent) ProtoMessage() {} + +func (x *OptimusChangeEvent) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_integration_v1beta1_event_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use OptimusChangeEvent.ProtoReflect.Descriptor instead. +func (*OptimusChangeEvent) Descriptor() ([]byte, []int) { + return file_raystack_optimus_integration_v1beta1_event_proto_rawDescGZIP(), []int{4} +} + +func (x *OptimusChangeEvent) GetEventId() string { + if x != nil { + return x.EventId + } + return "" +} + +func (x *OptimusChangeEvent) GetOccurredAt() *timestamppb.Timestamp { + if x != nil { + return x.OccurredAt + } + return nil +} + +func (x *OptimusChangeEvent) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *OptimusChangeEvent) GetNamespaceName() string { + if x != nil { + return x.NamespaceName + } + return "" +} + +func (x *OptimusChangeEvent) GetEventType() OptimusChangeEvent_EventType { + if x != nil { + return x.EventType + } + return OptimusChangeEvent_EVENT_TYPE_TYPE_UNSPECIFIED +} + +func (m *OptimusChangeEvent) GetPayload() isOptimusChangeEvent_Payload { + if m != nil { + return m.Payload + } + return nil +} + +func (x *OptimusChangeEvent) GetJobChange() *JobChangePayload { + if x, ok := x.GetPayload().(*OptimusChangeEvent_JobChange); ok { + return x.JobChange + } + return nil +} + +func (x *OptimusChangeEvent) GetResourceChange() *ResourceChangePayload { + if x, ok := x.GetPayload().(*OptimusChangeEvent_ResourceChange); ok { + return x.ResourceChange + } + return nil +} + +func (x *OptimusChangeEvent) GetJobRun() *JobRunPayload { + if x, ok := x.GetPayload().(*OptimusChangeEvent_JobRun); ok { + return x.JobRun + } + return nil +} + +func (x *OptimusChangeEvent) GetJobStateChange() *JobStateChangePayload { + if x, ok := x.GetPayload().(*OptimusChangeEvent_JobStateChange); ok { + return x.JobStateChange + } + return nil +} + +type isOptimusChangeEvent_Payload interface { + isOptimusChangeEvent_Payload() +} + +type OptimusChangeEvent_JobChange struct { + JobChange *JobChangePayload `protobuf:"bytes,6,opt,name=job_change,json=jobChange,proto3,oneof"` +} + +type OptimusChangeEvent_ResourceChange struct { + ResourceChange *ResourceChangePayload `protobuf:"bytes,7,opt,name=resource_change,json=resourceChange,proto3,oneof"` +} + +type OptimusChangeEvent_JobRun struct { + JobRun *JobRunPayload `protobuf:"bytes,8,opt,name=job_run,json=jobRun,proto3,oneof"` +} + +type OptimusChangeEvent_JobStateChange struct { + JobStateChange *JobStateChangePayload `protobuf:"bytes,9,opt,name=job_state_change,json=jobStateChange,proto3,oneof"` +} + +func (*OptimusChangeEvent_JobChange) isOptimusChangeEvent_Payload() {} + +func (*OptimusChangeEvent_ResourceChange) isOptimusChangeEvent_Payload() {} + +func (*OptimusChangeEvent_JobRun) isOptimusChangeEvent_Payload() {} + +func (*OptimusChangeEvent_JobStateChange) isOptimusChangeEvent_Payload() {} + +var File_raystack_optimus_integration_v1beta1_event_proto protoreflect.FileDescriptor + +var file_raystack_optimus_integration_v1beta1_event_proto_rawDesc = []byte{ + 0x0a, 0x33, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2f, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x27, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x1a, 0x1f, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x2f, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2f, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x2f, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2f, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2f, 0x6a, 0x6f, 0x62, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x22, 0x93, 0x01, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x64, + 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x53, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x7c, 0x0a, 0x10, 0x4a, 0x6f, 0x62, 0x43, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x6a, + 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, + 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x4d, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x73, 0x70, + 0x65, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x53, + 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x6a, 0x6f, + 0x62, 0x53, 0x70, 0x65, 0x63, 0x22, 0xc2, 0x01, 0x0a, 0x0d, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, + 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x3d, 0x0a, 0x0c, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x5f, + 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x41, + 0x74, 0x12, 0x1c, 0x0a, 0x0a, 0x6a, 0x6f, 0x62, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x12, + 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x74, 0x0a, 0x15, 0x4a, 0x6f, + 0x62, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x40, + 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2a, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, + 0x6d, 0x75, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x22, 0x88, 0x08, 0x0a, 0x12, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x43, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x49, 0x64, 0x12, 0x3b, 0x0a, 0x0b, 0x6f, 0x63, 0x63, 0x75, 0x72, 0x72, 0x65, 0x64, 0x5f, 0x61, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x6f, 0x63, 0x63, 0x75, 0x72, 0x72, 0x65, 0x64, 0x41, 0x74, 0x12, + 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x64, 0x0a, 0x0a, 0x65, 0x76, 0x65, + 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x45, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, + 0x6d, 0x75, 0x73, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x43, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x5a, 0x0a, 0x0a, 0x6a, 0x6f, 0x62, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, + 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, + 0x62, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x00, + 0x52, 0x09, 0x6a, 0x6f, 0x62, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x69, 0x0a, 0x0f, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x00, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x51, 0x0a, 0x07, 0x6a, 0x6f, 0x62, 0x5f, 0x72, 0x75, + 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x69, 0x6e, + 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, + 0x00, 0x52, 0x06, 0x6a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x12, 0x6a, 0x0a, 0x10, 0x6a, 0x6f, 0x62, + 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, + 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4a, 0x6f, + 0x62, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x48, 0x00, 0x52, 0x0e, 0x6a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0xd8, 0x02, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a, 0x1b, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, + 0x45, 0x44, 0x10, 0x00, 0x12, 0x1e, 0x0a, 0x1a, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x43, 0x52, 0x45, 0x41, + 0x54, 0x45, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x55, 0x50, 0x44, 0x41, + 0x54, 0x45, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x4a, 0x4f, 0x42, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x03, 0x12, + 0x19, 0x0a, 0x15, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4a, 0x4f, + 0x42, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x04, 0x12, 0x19, 0x0a, 0x15, 0x45, 0x56, + 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4a, 0x4f, 0x42, 0x5f, 0x44, 0x45, 0x4c, + 0x45, 0x54, 0x45, 0x10, 0x05, 0x12, 0x20, 0x0a, 0x1c, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x4a, 0x4f, 0x42, 0x5f, 0x57, 0x41, 0x49, 0x54, 0x5f, 0x55, 0x50, 0x53, + 0x54, 0x52, 0x45, 0x41, 0x4d, 0x10, 0x06, 0x12, 0x1e, 0x0a, 0x1a, 0x45, 0x56, 0x45, 0x4e, 0x54, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4a, 0x4f, 0x42, 0x5f, 0x49, 0x4e, 0x5f, 0x50, 0x52, 0x4f, + 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x07, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x56, 0x45, 0x4e, 0x54, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4a, 0x4f, 0x42, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, + 0x53, 0x10, 0x08, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x4a, 0x4f, 0x42, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x09, 0x12, + 0x1f, 0x0a, 0x1b, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4a, 0x4f, + 0x42, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x10, 0x0a, + 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x49, 0x0a, 0x1e, 0x63, + 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x42, 0x05, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x50, 0x01, 0x5a, 0x1e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, 0x6f, + 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_raystack_optimus_integration_v1beta1_event_proto_rawDescOnce sync.Once + file_raystack_optimus_integration_v1beta1_event_proto_rawDescData = file_raystack_optimus_integration_v1beta1_event_proto_rawDesc +) + +func file_raystack_optimus_integration_v1beta1_event_proto_rawDescGZIP() []byte { + file_raystack_optimus_integration_v1beta1_event_proto_rawDescOnce.Do(func() { + file_raystack_optimus_integration_v1beta1_event_proto_rawDescData = protoimpl.X.CompressGZIP(file_raystack_optimus_integration_v1beta1_event_proto_rawDescData) + }) + return file_raystack_optimus_integration_v1beta1_event_proto_rawDescData +} + +var file_raystack_optimus_integration_v1beta1_event_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_raystack_optimus_integration_v1beta1_event_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_raystack_optimus_integration_v1beta1_event_proto_goTypes = []interface{}{ + (OptimusChangeEvent_EventType)(0), // 0: raystack.optimus.integration.v1beta1.OptimusChangeEvent.EventType + (*ResourceChangePayload)(nil), // 1: raystack.optimus.integration.v1beta1.ResourceChangePayload + (*JobChangePayload)(nil), // 2: raystack.optimus.integration.v1beta1.JobChangePayload + (*JobRunPayload)(nil), // 3: raystack.optimus.integration.v1beta1.JobRunPayload + (*JobStateChangePayload)(nil), // 4: raystack.optimus.integration.v1beta1.JobStateChangePayload + (*OptimusChangeEvent)(nil), // 5: raystack.optimus.integration.v1beta1.OptimusChangeEvent + (*v1beta1.ResourceSpecification)(nil), // 6: raystack.optimus.core.v1beta1.ResourceSpecification + (*v1beta1.JobSpecification)(nil), // 7: raystack.optimus.core.v1beta1.JobSpecification + (*timestamppb.Timestamp)(nil), // 8: google.protobuf.Timestamp + (v1beta1.JobState)(0), // 9: raystack.optimus.core.v1beta1.JobState +} +var file_raystack_optimus_integration_v1beta1_event_proto_depIdxs = []int32{ + 6, // 0: raystack.optimus.integration.v1beta1.ResourceChangePayload.resource:type_name -> raystack.optimus.core.v1beta1.ResourceSpecification + 7, // 1: raystack.optimus.integration.v1beta1.JobChangePayload.job_spec:type_name -> raystack.optimus.core.v1beta1.JobSpecification + 8, // 2: raystack.optimus.integration.v1beta1.JobRunPayload.scheduled_at:type_name -> google.protobuf.Timestamp + 8, // 3: raystack.optimus.integration.v1beta1.JobRunPayload.start_time:type_name -> google.protobuf.Timestamp + 9, // 4: raystack.optimus.integration.v1beta1.JobStateChangePayload.state:type_name -> raystack.optimus.core.v1beta1.JobState + 8, // 5: raystack.optimus.integration.v1beta1.OptimusChangeEvent.occurred_at:type_name -> google.protobuf.Timestamp + 0, // 6: raystack.optimus.integration.v1beta1.OptimusChangeEvent.event_type:type_name -> raystack.optimus.integration.v1beta1.OptimusChangeEvent.EventType + 2, // 7: raystack.optimus.integration.v1beta1.OptimusChangeEvent.job_change:type_name -> raystack.optimus.integration.v1beta1.JobChangePayload + 1, // 8: raystack.optimus.integration.v1beta1.OptimusChangeEvent.resource_change:type_name -> raystack.optimus.integration.v1beta1.ResourceChangePayload + 3, // 9: raystack.optimus.integration.v1beta1.OptimusChangeEvent.job_run:type_name -> raystack.optimus.integration.v1beta1.JobRunPayload + 4, // 10: raystack.optimus.integration.v1beta1.OptimusChangeEvent.job_state_change:type_name -> raystack.optimus.integration.v1beta1.JobStateChangePayload + 11, // [11:11] is the sub-list for method output_type + 11, // [11:11] is the sub-list for method input_type + 11, // [11:11] is the sub-list for extension type_name + 11, // [11:11] is the sub-list for extension extendee + 0, // [0:11] is the sub-list for field type_name +} + +func init() { file_raystack_optimus_integration_v1beta1_event_proto_init() } +func file_raystack_optimus_integration_v1beta1_event_proto_init() { + if File_raystack_optimus_integration_v1beta1_event_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_raystack_optimus_integration_v1beta1_event_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResourceChangePayload); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_integration_v1beta1_event_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*JobChangePayload); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_integration_v1beta1_event_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*JobRunPayload); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_integration_v1beta1_event_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*JobStateChangePayload); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_integration_v1beta1_event_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OptimusChangeEvent); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_raystack_optimus_integration_v1beta1_event_proto_msgTypes[4].OneofWrappers = []interface{}{ + (*OptimusChangeEvent_JobChange)(nil), + (*OptimusChangeEvent_ResourceChange)(nil), + (*OptimusChangeEvent_JobRun)(nil), + (*OptimusChangeEvent_JobStateChange)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_raystack_optimus_integration_v1beta1_event_proto_rawDesc, + NumEnums: 1, + NumMessages: 5, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_raystack_optimus_integration_v1beta1_event_proto_goTypes, + DependencyIndexes: file_raystack_optimus_integration_v1beta1_event_proto_depIdxs, + EnumInfos: file_raystack_optimus_integration_v1beta1_event_proto_enumTypes, + MessageInfos: file_raystack_optimus_integration_v1beta1_event_proto_msgTypes, + }.Build() + File_raystack_optimus_integration_v1beta1_event_proto = out.File + file_raystack_optimus_integration_v1beta1_event_proto_rawDesc = nil + file_raystack_optimus_integration_v1beta1_event_proto_goTypes = nil + file_raystack_optimus_integration_v1beta1_event_proto_depIdxs = nil +} diff --git a/protos/odpf/optimus/cluster/v1beta1/command.swagger.json b/protos/raystack/optimus/integration/v1beta1/event.swagger.json similarity index 82% rename from protos/odpf/optimus/cluster/v1beta1/command.swagger.json rename to protos/raystack/optimus/integration/v1beta1/event.swagger.json index 773c449a06..c3e20136a8 100644 --- a/protos/odpf/optimus/cluster/v1beta1/command.swagger.json +++ b/protos/raystack/optimus/integration/v1beta1/event.swagger.json @@ -1,15 +1,11 @@ { "swagger": "2.0", "info": { - "title": "odpf/optimus/cluster/v1beta1/command.proto", + "title": "raystack/optimus/integration/v1beta1/event.proto", "version": "version not set" }, - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], + "consumes": ["application/json"], + "produces": ["application/json"], "paths": {}, "definitions": { "protobufAny": { diff --git a/protos/raystack/optimus/plugins/v1beta1/dependency_resolver.pb.go b/protos/raystack/optimus/plugins/v1beta1/dependency_resolver.pb.go new file mode 100644 index 0000000000..364f37d5c4 --- /dev/null +++ b/protos/raystack/optimus/plugins/v1beta1/dependency_resolver.pb.go @@ -0,0 +1,1204 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.0 +// protoc (unknown) +// source: raystack/optimus/plugins/v1beta1/dependency_resolver.proto + +package optimus + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type GetNameRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *GetNameRequest) Reset() { + *x = GetNameRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetNameRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetNameRequest) ProtoMessage() {} + +func (x *GetNameRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetNameRequest.ProtoReflect.Descriptor instead. +func (*GetNameRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{0} +} + +type GetNameResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *GetNameResponse) Reset() { + *x = GetNameResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetNameResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetNameResponse) ProtoMessage() {} + +func (x *GetNameResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetNameResponse.ProtoReflect.Descriptor instead. +func (*GetNameResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{1} +} + +func (x *GetNameResponse) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type GenerateDestinationRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Config *Configs `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` + Assets *Assets `protobuf:"bytes,2,opt,name=assets,proto3" json:"assets,omitempty"` + Options *PluginOptions `protobuf:"bytes,40,opt,name=options,proto3" json:"options,omitempty"` +} + +func (x *GenerateDestinationRequest) Reset() { + *x = GenerateDestinationRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GenerateDestinationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenerateDestinationRequest) ProtoMessage() {} + +func (x *GenerateDestinationRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GenerateDestinationRequest.ProtoReflect.Descriptor instead. +func (*GenerateDestinationRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{2} +} + +func (x *GenerateDestinationRequest) GetConfig() *Configs { + if x != nil { + return x.Config + } + return nil +} + +func (x *GenerateDestinationRequest) GetAssets() *Assets { + if x != nil { + return x.Assets + } + return nil +} + +func (x *GenerateDestinationRequest) GetOptions() *PluginOptions { + if x != nil { + return x.Options + } + return nil +} + +type GenerateDestinationResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Destination string `protobuf:"bytes,1,opt,name=destination,proto3" json:"destination,omitempty"` + DestinationType string `protobuf:"bytes,2,opt,name=destination_type,json=destinationType,proto3" json:"destination_type,omitempty"` +} + +func (x *GenerateDestinationResponse) Reset() { + *x = GenerateDestinationResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GenerateDestinationResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenerateDestinationResponse) ProtoMessage() {} + +func (x *GenerateDestinationResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GenerateDestinationResponse.ProtoReflect.Descriptor instead. +func (*GenerateDestinationResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{3} +} + +func (x *GenerateDestinationResponse) GetDestination() string { + if x != nil { + return x.Destination + } + return "" +} + +func (x *GenerateDestinationResponse) GetDestinationType() string { + if x != nil { + return x.DestinationType + } + return "" +} + +type GenerateDependenciesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Config *Configs `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` + Assets *Assets `protobuf:"bytes,2,opt,name=assets,proto3" json:"assets,omitempty"` + Options *PluginOptions `protobuf:"bytes,40,opt,name=options,proto3" json:"options,omitempty"` +} + +func (x *GenerateDependenciesRequest) Reset() { + *x = GenerateDependenciesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GenerateDependenciesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenerateDependenciesRequest) ProtoMessage() {} + +func (x *GenerateDependenciesRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GenerateDependenciesRequest.ProtoReflect.Descriptor instead. +func (*GenerateDependenciesRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{4} +} + +func (x *GenerateDependenciesRequest) GetConfig() *Configs { + if x != nil { + return x.Config + } + return nil +} + +func (x *GenerateDependenciesRequest) GetAssets() *Assets { + if x != nil { + return x.Assets + } + return nil +} + +func (x *GenerateDependenciesRequest) GetOptions() *PluginOptions { + if x != nil { + return x.Options + } + return nil +} + +type GenerateDependenciesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Dependencies []string `protobuf:"bytes,1,rep,name=dependencies,proto3" json:"dependencies,omitempty"` +} + +func (x *GenerateDependenciesResponse) Reset() { + *x = GenerateDependenciesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GenerateDependenciesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenerateDependenciesResponse) ProtoMessage() {} + +func (x *GenerateDependenciesResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GenerateDependenciesResponse.ProtoReflect.Descriptor instead. +func (*GenerateDependenciesResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{5} +} + +func (x *GenerateDependenciesResponse) GetDependencies() []string { + if x != nil { + return x.Dependencies + } + return nil +} + +type Configs struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Configs []*Configs_Config `protobuf:"bytes,1,rep,name=configs,proto3" json:"configs,omitempty"` +} + +func (x *Configs) Reset() { + *x = Configs{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Configs) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Configs) ProtoMessage() {} + +func (x *Configs) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Configs.ProtoReflect.Descriptor instead. +func (*Configs) Descriptor() ([]byte, []int) { + return file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{6} +} + +func (x *Configs) GetConfigs() []*Configs_Config { + if x != nil { + return x.Configs + } + return nil +} + +type Assets struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Assets []*Assets_Asset `protobuf:"bytes,1,rep,name=assets,proto3" json:"assets,omitempty"` +} + +func (x *Assets) Reset() { + *x = Assets{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Assets) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Assets) ProtoMessage() {} + +func (x *Assets) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Assets.ProtoReflect.Descriptor instead. +func (*Assets) Descriptor() ([]byte, []int) { + return file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{7} +} + +func (x *Assets) GetAssets() []*Assets_Asset { + if x != nil { + return x.Assets + } + return nil +} + +type InstanceData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` +} + +func (x *InstanceData) Reset() { + *x = InstanceData{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InstanceData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InstanceData) ProtoMessage() {} + +func (x *InstanceData) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InstanceData.ProtoReflect.Descriptor instead. +func (*InstanceData) Descriptor() ([]byte, []int) { + return file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{8} +} + +func (x *InstanceData) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *InstanceData) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +func (x *InstanceData) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +type CompileAssetsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Configs *Configs `protobuf:"bytes,1,opt,name=configs,proto3" json:"configs,omitempty"` + Assets *Assets `protobuf:"bytes,2,opt,name=assets,proto3" json:"assets,omitempty"` + InstanceData []*InstanceData `protobuf:"bytes,8,rep,name=instance_data,json=instanceData,proto3" json:"instance_data,omitempty"` + StartTime *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` + Options *PluginOptions `protobuf:"bytes,40,opt,name=options,proto3" json:"options,omitempty"` +} + +func (x *CompileAssetsRequest) Reset() { + *x = CompileAssetsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CompileAssetsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CompileAssetsRequest) ProtoMessage() {} + +func (x *CompileAssetsRequest) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CompileAssetsRequest.ProtoReflect.Descriptor instead. +func (*CompileAssetsRequest) Descriptor() ([]byte, []int) { + return file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{9} +} + +func (x *CompileAssetsRequest) GetConfigs() *Configs { + if x != nil { + return x.Configs + } + return nil +} + +func (x *CompileAssetsRequest) GetAssets() *Assets { + if x != nil { + return x.Assets + } + return nil +} + +func (x *CompileAssetsRequest) GetInstanceData() []*InstanceData { + if x != nil { + return x.InstanceData + } + return nil +} + +func (x *CompileAssetsRequest) GetStartTime() *timestamppb.Timestamp { + if x != nil { + return x.StartTime + } + return nil +} + +func (x *CompileAssetsRequest) GetEndTime() *timestamppb.Timestamp { + if x != nil { + return x.EndTime + } + return nil +} + +func (x *CompileAssetsRequest) GetOptions() *PluginOptions { + if x != nil { + return x.Options + } + return nil +} + +type CompileAssetsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Assets *Assets `protobuf:"bytes,1,opt,name=assets,proto3" json:"assets,omitempty"` +} + +func (x *CompileAssetsResponse) Reset() { + *x = CompileAssetsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CompileAssetsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CompileAssetsResponse) ProtoMessage() {} + +func (x *CompileAssetsResponse) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CompileAssetsResponse.ProtoReflect.Descriptor instead. +func (*CompileAssetsResponse) Descriptor() ([]byte, []int) { + return file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{10} +} + +func (x *CompileAssetsResponse) GetAssets() *Assets { + if x != nil { + return x.Assets + } + return nil +} + +type PluginOptions struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + DryRun bool `protobuf:"varint,1,opt,name=dry_run,json=dryRun,proto3" json:"dry_run,omitempty"` +} + +func (x *PluginOptions) Reset() { + *x = PluginOptions{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PluginOptions) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PluginOptions) ProtoMessage() {} + +func (x *PluginOptions) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PluginOptions.ProtoReflect.Descriptor instead. +func (*PluginOptions) Descriptor() ([]byte, []int) { + return file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{11} +} + +func (x *PluginOptions) GetDryRun() bool { + if x != nil { + return x.DryRun + } + return false +} + +type Configs_Config struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *Configs_Config) Reset() { + *x = Configs_Config{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Configs_Config) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Configs_Config) ProtoMessage() {} + +func (x *Configs_Config) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Configs_Config.ProtoReflect.Descriptor instead. +func (*Configs_Config) Descriptor() ([]byte, []int) { + return file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{6, 0} +} + +func (x *Configs_Config) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Configs_Config) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +type Assets_Asset struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *Assets_Asset) Reset() { + *x = Assets_Asset{} + if protoimpl.UnsafeEnabled { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Assets_Asset) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Assets_Asset) ProtoMessage() {} + +func (x *Assets_Asset) ProtoReflect() protoreflect.Message { + mi := &file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Assets_Asset.ProtoReflect.Descriptor instead. +func (*Assets_Asset) Descriptor() ([]byte, []int) { + return file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP(), []int{7, 0} +} + +func (x *Assets_Asset) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Assets_Asset) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +var File_raystack_optimus_plugins_v1beta1_dependency_resolver_proto protoreflect.FileDescriptor + +var file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_rawDesc = []byte{ + 0x0a, 0x3d, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2f, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2f, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, + 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x23, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x10, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x25, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x4e, 0x61, + 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xfb, + 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x44, 0x65, 0x73, 0x74, 0x69, + 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, + 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, + 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, 0x06, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x12, 0x43, 0x0a, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, + 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, + 0x52, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x4c, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x6a, 0x0a, 0x1b, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, + 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, + 0x10, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x22, 0xfc, 0x01, 0x0a, 0x1b, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x43, + 0x0a, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, + 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x06, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x73, 0x12, 0x4c, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x28, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x42, 0x0a, 0x1c, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x65, 0x70, 0x65, 0x6e, + 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x64, + 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x22, 0x8c, 0x01, 0x0a, 0x07, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x4d, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x1a, 0x32, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x86, 0x01, 0x0a, 0x06, 0x41, + 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x49, 0x0a, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x73, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, + 0x1a, 0x31, 0x0a, 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x22, 0x4c, 0x0a, 0x0c, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x44, + 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x22, 0xcd, 0x03, 0x0a, 0x14, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x46, 0x0a, 0x07, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x67, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, + 0x73, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x73, 0x12, 0x43, 0x0a, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, + 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x56, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, + 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x44, 0x61, 0x74, + 0x61, 0x52, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, + 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x65, 0x6e, + 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, + 0x65, 0x12, 0x4c, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x28, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x4f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4a, + 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, + 0x06, 0x22, 0x5c, 0x0a, 0x15, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x06, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x67, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, + 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x22, + 0x28, 0x0a, 0x0d, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x17, 0x0a, 0x07, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x32, 0xd6, 0x04, 0x0a, 0x1c, 0x44, 0x65, + 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, + 0x4d, 0x6f, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x74, 0x0a, 0x07, 0x47, 0x65, + 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x33, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, + 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x67, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, + 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x98, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x44, 0x65, 0x73, + 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3f, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x40, 0x2e, 0x67, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x9b, 0x01, 0x0a, 0x14, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, + 0x63, 0x69, 0x65, 0x73, 0x12, 0x40, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x41, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x86, 0x01, 0x0a, 0x0d, 0x43, 0x6f, + 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x39, 0x2e, 0x67, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, + 0x73, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3a, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6d, + 0x70, 0x69, 0x6c, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x42, 0x66, 0x0a, 0x26, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2e, 0x6f, 0x70, 0x74, + 0x69, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x42, 0x1a, 0x44, 0x65, + 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, + 0x4d, 0x6f, 0x64, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x1e, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x6e, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x75, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescOnce sync.Once + file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescData = file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_rawDesc +) + +func file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescGZIP() []byte { + file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescOnce.Do(func() { + file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescData = protoimpl.X.CompressGZIP(file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescData) + }) + return file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_rawDescData +} + +var file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes = make([]protoimpl.MessageInfo, 14) +var file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_goTypes = []interface{}{ + (*GetNameRequest)(nil), // 0: raystack.optimus.plugins.v1beta1.GetNameRequest + (*GetNameResponse)(nil), // 1: raystack.optimus.plugins.v1beta1.GetNameResponse + (*GenerateDestinationRequest)(nil), // 2: raystack.optimus.plugins.v1beta1.GenerateDestinationRequest + (*GenerateDestinationResponse)(nil), // 3: raystack.optimus.plugins.v1beta1.GenerateDestinationResponse + (*GenerateDependenciesRequest)(nil), // 4: raystack.optimus.plugins.v1beta1.GenerateDependenciesRequest + (*GenerateDependenciesResponse)(nil), // 5: raystack.optimus.plugins.v1beta1.GenerateDependenciesResponse + (*Configs)(nil), // 6: raystack.optimus.plugins.v1beta1.Configs + (*Assets)(nil), // 7: raystack.optimus.plugins.v1beta1.Assets + (*InstanceData)(nil), // 8: raystack.optimus.plugins.v1beta1.InstanceData + (*CompileAssetsRequest)(nil), // 9: raystack.optimus.plugins.v1beta1.CompileAssetsRequest + (*CompileAssetsResponse)(nil), // 10: raystack.optimus.plugins.v1beta1.CompileAssetsResponse + (*PluginOptions)(nil), // 11: raystack.optimus.plugins.v1beta1.PluginOptions + (*Configs_Config)(nil), // 12: raystack.optimus.plugins.v1beta1.Configs.Config + (*Assets_Asset)(nil), // 13: raystack.optimus.plugins.v1beta1.Assets.Asset + (*timestamppb.Timestamp)(nil), // 14: google.protobuf.Timestamp +} +var file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_depIdxs = []int32{ + 6, // 0: raystack.optimus.plugins.v1beta1.GenerateDestinationRequest.config:type_name -> raystack.optimus.plugins.v1beta1.Configs + 7, // 1: raystack.optimus.plugins.v1beta1.GenerateDestinationRequest.assets:type_name -> raystack.optimus.plugins.v1beta1.Assets + 11, // 2: raystack.optimus.plugins.v1beta1.GenerateDestinationRequest.options:type_name -> raystack.optimus.plugins.v1beta1.PluginOptions + 6, // 3: raystack.optimus.plugins.v1beta1.GenerateDependenciesRequest.config:type_name -> raystack.optimus.plugins.v1beta1.Configs + 7, // 4: raystack.optimus.plugins.v1beta1.GenerateDependenciesRequest.assets:type_name -> raystack.optimus.plugins.v1beta1.Assets + 11, // 5: raystack.optimus.plugins.v1beta1.GenerateDependenciesRequest.options:type_name -> raystack.optimus.plugins.v1beta1.PluginOptions + 12, // 6: raystack.optimus.plugins.v1beta1.Configs.configs:type_name -> raystack.optimus.plugins.v1beta1.Configs.Config + 13, // 7: raystack.optimus.plugins.v1beta1.Assets.assets:type_name -> raystack.optimus.plugins.v1beta1.Assets.Asset + 6, // 8: raystack.optimus.plugins.v1beta1.CompileAssetsRequest.configs:type_name -> raystack.optimus.plugins.v1beta1.Configs + 7, // 9: raystack.optimus.plugins.v1beta1.CompileAssetsRequest.assets:type_name -> raystack.optimus.plugins.v1beta1.Assets + 8, // 10: raystack.optimus.plugins.v1beta1.CompileAssetsRequest.instance_data:type_name -> raystack.optimus.plugins.v1beta1.InstanceData + 14, // 11: raystack.optimus.plugins.v1beta1.CompileAssetsRequest.start_time:type_name -> google.protobuf.Timestamp + 14, // 12: raystack.optimus.plugins.v1beta1.CompileAssetsRequest.end_time:type_name -> google.protobuf.Timestamp + 11, // 13: raystack.optimus.plugins.v1beta1.CompileAssetsRequest.options:type_name -> raystack.optimus.plugins.v1beta1.PluginOptions + 7, // 14: raystack.optimus.plugins.v1beta1.CompileAssetsResponse.assets:type_name -> raystack.optimus.plugins.v1beta1.Assets + 0, // 15: raystack.optimus.plugins.v1beta1.DependencyResolverModService.GetName:input_type -> raystack.optimus.plugins.v1beta1.GetNameRequest + 2, // 16: raystack.optimus.plugins.v1beta1.DependencyResolverModService.GenerateDestination:input_type -> raystack.optimus.plugins.v1beta1.GenerateDestinationRequest + 4, // 17: raystack.optimus.plugins.v1beta1.DependencyResolverModService.GenerateDependencies:input_type -> raystack.optimus.plugins.v1beta1.GenerateDependenciesRequest + 9, // 18: raystack.optimus.plugins.v1beta1.DependencyResolverModService.CompileAssets:input_type -> raystack.optimus.plugins.v1beta1.CompileAssetsRequest + 1, // 19: raystack.optimus.plugins.v1beta1.DependencyResolverModService.GetName:output_type -> raystack.optimus.plugins.v1beta1.GetNameResponse + 3, // 20: raystack.optimus.plugins.v1beta1.DependencyResolverModService.GenerateDestination:output_type -> raystack.optimus.plugins.v1beta1.GenerateDestinationResponse + 5, // 21: raystack.optimus.plugins.v1beta1.DependencyResolverModService.GenerateDependencies:output_type -> raystack.optimus.plugins.v1beta1.GenerateDependenciesResponse + 10, // 22: raystack.optimus.plugins.v1beta1.DependencyResolverModService.CompileAssets:output_type -> raystack.optimus.plugins.v1beta1.CompileAssetsResponse + 19, // [19:23] is the sub-list for method output_type + 15, // [15:19] is the sub-list for method input_type + 15, // [15:15] is the sub-list for extension type_name + 15, // [15:15] is the sub-list for extension extendee + 0, // [0:15] is the sub-list for field type_name +} + +func init() { file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_init() } +func file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_init() { + if File_raystack_optimus_plugins_v1beta1_dependency_resolver_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetNameRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetNameResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GenerateDestinationRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GenerateDestinationResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GenerateDependenciesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GenerateDependenciesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Configs); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Assets); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InstanceData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CompileAssetsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CompileAssetsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PluginOptions); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Configs_Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Assets_Asset); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_rawDesc, + NumEnums: 0, + NumMessages: 14, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_goTypes, + DependencyIndexes: file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_depIdxs, + MessageInfos: file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_msgTypes, + }.Build() + File_raystack_optimus_plugins_v1beta1_dependency_resolver_proto = out.File + file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_rawDesc = nil + file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_goTypes = nil + file_raystack_optimus_plugins_v1beta1_dependency_resolver_proto_depIdxs = nil +} diff --git a/protos/odpf/optimus/plugins/v1beta1/dependency_resolver.swagger.json b/protos/raystack/optimus/plugins/v1beta1/dependency_resolver.swagger.json similarity index 66% rename from protos/odpf/optimus/plugins/v1beta1/dependency_resolver.swagger.json rename to protos/raystack/optimus/plugins/v1beta1/dependency_resolver.swagger.json index f928c5fbd0..2dbf75bf8b 100644 --- a/protos/odpf/optimus/plugins/v1beta1/dependency_resolver.swagger.json +++ b/protos/raystack/optimus/plugins/v1beta1/dependency_resolver.swagger.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "title": "odpf/optimus/plugins/v1beta1/dependency_resolver.proto", + "title": "raystack/optimus/plugins/v1beta1/dependency_resolver.proto", "version": "version not set" }, "tags": [ @@ -9,12 +9,8 @@ "name": "DependencyResolverModService" } ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], + "consumes": ["application/json"], + "produces": ["application/json"], "paths": {}, "definitions": { "AssetsAsset": { @@ -39,17 +35,6 @@ } } }, - "ProjectSpecificationProjectSecret": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, "protobufAny": { "type": "object", "properties": { @@ -140,7 +125,7 @@ } } }, - "v1beta1InstanceSpecData": { + "v1beta1InstanceData": { "type": "object", "properties": { "name": { @@ -150,20 +135,10 @@ "type": "string" }, "type": { - "$ref": "#/definitions/v1beta1InstanceSpecDataType" + "type": "string" } } }, - "v1beta1InstanceSpecDataType": { - "type": "string", - "enum": [ - "TYPE_UNSPECIFIED", - "TYPE_ENV", - "TYPE_FILE" - ], - "default": "TYPE_UNSPECIFIED", - "title": "type of data, could be an env var or file" - }, "v1beta1PluginOptions": { "type": "object", "properties": { @@ -171,40 +146,6 @@ "type": "boolean" } } - }, - "v1beta1ProjectSpecification": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "config": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "secrets": { - "type": "array", - "items": { - "$ref": "#/definitions/ProjectSpecificationProjectSecret" - } - } - } - }, - "v1beta1TaskWindow": { - "type": "object", - "properties": { - "size": { - "type": "string" - }, - "offset": { - "type": "string" - }, - "truncateTo": { - "type": "string" - } - } } } } diff --git a/protos/odpf/optimus/plugins/v1beta1/dependency_resolver_grpc.pb.go b/protos/raystack/optimus/plugins/v1beta1/dependency_resolver_grpc.pb.go similarity index 89% rename from protos/odpf/optimus/plugins/v1beta1/dependency_resolver_grpc.pb.go rename to protos/raystack/optimus/plugins/v1beta1/dependency_resolver_grpc.pb.go index 053597a21c..1a6d37cad9 100644 --- a/protos/odpf/optimus/plugins/v1beta1/dependency_resolver_grpc.pb.go +++ b/protos/raystack/optimus/plugins/v1beta1/dependency_resolver_grpc.pb.go @@ -2,7 +2,7 @@ // versions: // - protoc-gen-go-grpc v1.2.0 // - protoc (unknown) -// source: odpf/optimus/plugins/v1beta1/dependency_resolver.proto +// source: raystack/optimus/plugins/v1beta1/dependency_resolver.proto package optimus @@ -43,7 +43,7 @@ func NewDependencyResolverModServiceClient(cc grpc.ClientConnInterface) Dependen func (c *dependencyResolverModServiceClient) GetName(ctx context.Context, in *GetNameRequest, opts ...grpc.CallOption) (*GetNameResponse, error) { out := new(GetNameResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.plugins.v1beta1.DependencyResolverModService/GetName", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.plugins.v1beta1.DependencyResolverModService/GetName", in, out, opts...) if err != nil { return nil, err } @@ -52,7 +52,7 @@ func (c *dependencyResolverModServiceClient) GetName(ctx context.Context, in *Ge func (c *dependencyResolverModServiceClient) GenerateDestination(ctx context.Context, in *GenerateDestinationRequest, opts ...grpc.CallOption) (*GenerateDestinationResponse, error) { out := new(GenerateDestinationResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.plugins.v1beta1.DependencyResolverModService/GenerateDestination", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.plugins.v1beta1.DependencyResolverModService/GenerateDestination", in, out, opts...) if err != nil { return nil, err } @@ -61,7 +61,7 @@ func (c *dependencyResolverModServiceClient) GenerateDestination(ctx context.Con func (c *dependencyResolverModServiceClient) GenerateDependencies(ctx context.Context, in *GenerateDependenciesRequest, opts ...grpc.CallOption) (*GenerateDependenciesResponse, error) { out := new(GenerateDependenciesResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.plugins.v1beta1.DependencyResolverModService/GenerateDependencies", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.plugins.v1beta1.DependencyResolverModService/GenerateDependencies", in, out, opts...) if err != nil { return nil, err } @@ -70,7 +70,7 @@ func (c *dependencyResolverModServiceClient) GenerateDependencies(ctx context.Co func (c *dependencyResolverModServiceClient) CompileAssets(ctx context.Context, in *CompileAssetsRequest, opts ...grpc.CallOption) (*CompileAssetsResponse, error) { out := new(CompileAssetsResponse) - err := c.cc.Invoke(ctx, "/odpf.optimus.plugins.v1beta1.DependencyResolverModService/CompileAssets", in, out, opts...) + err := c.cc.Invoke(ctx, "/raystack.optimus.plugins.v1beta1.DependencyResolverModService/CompileAssets", in, out, opts...) if err != nil { return nil, err } @@ -133,7 +133,7 @@ func _DependencyResolverModService_GetName_Handler(srv interface{}, ctx context. } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.plugins.v1beta1.DependencyResolverModService/GetName", + FullMethod: "/raystack.optimus.plugins.v1beta1.DependencyResolverModService/GetName", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(DependencyResolverModServiceServer).GetName(ctx, req.(*GetNameRequest)) @@ -151,7 +151,7 @@ func _DependencyResolverModService_GenerateDestination_Handler(srv interface{}, } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.plugins.v1beta1.DependencyResolverModService/GenerateDestination", + FullMethod: "/raystack.optimus.plugins.v1beta1.DependencyResolverModService/GenerateDestination", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(DependencyResolverModServiceServer).GenerateDestination(ctx, req.(*GenerateDestinationRequest)) @@ -169,7 +169,7 @@ func _DependencyResolverModService_GenerateDependencies_Handler(srv interface{}, } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.plugins.v1beta1.DependencyResolverModService/GenerateDependencies", + FullMethod: "/raystack.optimus.plugins.v1beta1.DependencyResolverModService/GenerateDependencies", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(DependencyResolverModServiceServer).GenerateDependencies(ctx, req.(*GenerateDependenciesRequest)) @@ -187,7 +187,7 @@ func _DependencyResolverModService_CompileAssets_Handler(srv interface{}, ctx co } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.optimus.plugins.v1beta1.DependencyResolverModService/CompileAssets", + FullMethod: "/raystack.optimus.plugins.v1beta1.DependencyResolverModService/CompileAssets", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(DependencyResolverModServiceServer).CompileAssets(ctx, req.(*CompileAssetsRequest)) @@ -199,7 +199,7 @@ func _DependencyResolverModService_CompileAssets_Handler(srv interface{}, ctx co // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var DependencyResolverModService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "odpf.optimus.plugins.v1beta1.DependencyResolverModService", + ServiceName: "raystack.optimus.plugins.v1beta1.DependencyResolverModService", HandlerType: (*DependencyResolverModServiceServer)(nil), Methods: []grpc.MethodDesc{ { @@ -220,5 +220,5 @@ var DependencyResolverModService_ServiceDesc = grpc.ServiceDesc{ }, }, Streams: []grpc.StreamDesc{}, - Metadata: "odpf/optimus/plugins/v1beta1/dependency_resolver.proto", + Metadata: "raystack/optimus/plugins/v1beta1/dependency_resolver.proto", } diff --git a/sdk/go.mod b/sdk/go.mod index ade720ac0e..639690f342 100644 --- a/sdk/go.mod +++ b/sdk/go.mod @@ -1,4 +1,4 @@ -module github.com/odpf/optimus/sdk +module github.com/raystack/optimus/sdk go 1.18 diff --git a/sdk/plugin/mock/mods.go b/sdk/plugin/mock/mods.go index 6b54c276a8..74e6399c61 100644 --- a/sdk/plugin/mock/mods.go +++ b/sdk/plugin/mock/mods.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/mock" - "github.com/odpf/optimus/sdk/plugin" + "github.com/raystack/optimus/sdk/plugin" ) type YamlMod struct { diff --git a/sdk/plugin/mock/plugin.go b/sdk/plugin/mock/plugin.go index fd4b45874b..110ea95a35 100644 --- a/sdk/plugin/mock/plugin.go +++ b/sdk/plugin/mock/plugin.go @@ -3,7 +3,7 @@ package mock import ( "context" - "github.com/odpf/optimus/sdk/plugin" + "github.com/raystack/optimus/sdk/plugin" ) func NewMockBinaryPlugin(name, pluginType string) *plugin.Plugin { diff --git a/sdk/plugin/plugin_test.go b/sdk/plugin/plugin_test.go index 5182516558..8ec4dfa907 100644 --- a/sdk/plugin/plugin_test.go +++ b/sdk/plugin/plugin_test.go @@ -6,8 +6,8 @@ import ( "github.com/stretchr/testify/assert" - "github.com/odpf/optimus/sdk/plugin" - "github.com/odpf/optimus/sdk/plugin/mock" + "github.com/raystack/optimus/sdk/plugin" + "github.com/raystack/optimus/sdk/plugin/mock" ) func TestPlugins(t *testing.T) { @@ -47,7 +47,7 @@ func TestPlugins(t *testing.T) { err: errors.New("plugin name cannot be empty"), info: plugin.Info{ Name: "", - Image: "odpf.io/example", + Image: "raystack.io/example", PluginVersion: "0.2", Entrypoint: plugin.Entrypoint{ Script: "sleep 10", @@ -73,7 +73,7 @@ func TestPlugins(t *testing.T) { err: errors.New("plugin version cannot be empty"), info: plugin.Info{ Name: "example", - Image: "odpf.io/example", + Image: "raystack.io/example", PluginVersion: "", Entrypoint: plugin.Entrypoint{ Script: "sleep 10", @@ -86,7 +86,7 @@ func TestPlugins(t *testing.T) { err: errors.New("entrypoint args cannot be empty"), info: plugin.Info{ Name: "example", - Image: "odpf.io/example", + Image: "raystack.io/example", PluginVersion: "0.2", Entrypoint: plugin.Entrypoint{}, PluginType: plugin.TypeTask, @@ -97,7 +97,7 @@ func TestPlugins(t *testing.T) { err: errors.New("plugin type is not supported"), info: plugin.Info{ Name: "example", - Image: "odpf.io/example", + Image: "raystack.io/example", PluginVersion: "0.2", Entrypoint: plugin.Entrypoint{ Script: "sleep 10", @@ -110,7 +110,7 @@ func TestPlugins(t *testing.T) { err: nil, info: plugin.Info{ Name: "example", - Image: "odpf.io/example", + Image: "raystack.io/example", PluginVersion: "0.2", Entrypoint: plugin.Entrypoint{ Script: "sleep 10", diff --git a/server/cmd/migration/migrate_to.go b/server/cmd/migration/migrate_to.go index 32cb7048d1..cba6572ad6 100644 --- a/server/cmd/migration/migrate_to.go +++ b/server/cmd/migration/migrate_to.go @@ -5,8 +5,8 @@ import ( "github.com/spf13/cobra" - "github.com/odpf/optimus/config" - "github.com/odpf/optimus/internal/store/postgres" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/internal/store/postgres" ) type migrateTo struct { diff --git a/server/cmd/migration/rollback.go b/server/cmd/migration/rollback.go index a0cf374d35..f9488c5195 100644 --- a/server/cmd/migration/rollback.go +++ b/server/cmd/migration/rollback.go @@ -5,8 +5,8 @@ import ( "github.com/spf13/cobra" - "github.com/odpf/optimus/config" - "github.com/odpf/optimus/internal/store/postgres" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/internal/store/postgres" ) type rollbackCommand struct { diff --git a/server/cmd/serve.go b/server/cmd/serve.go index fa228f4bfc..eaad343b9a 100644 --- a/server/cmd/serve.go +++ b/server/cmd/serve.go @@ -8,9 +8,9 @@ import ( "github.com/spf13/cobra" - "github.com/odpf/optimus/config" - oplugin "github.com/odpf/optimus/plugin" - "github.com/odpf/optimus/server" + "github.com/raystack/optimus/config" + oplugin "github.com/raystack/optimus/plugin" + "github.com/raystack/optimus/server" ) type serveCommand struct { diff --git a/server/handler/v1beta1/version.go b/server/handler/v1beta1/version.go index 3534bf51b9..774fb4f9c6 100644 --- a/server/handler/v1beta1/version.go +++ b/server/handler/v1beta1/version.go @@ -3,9 +3,9 @@ package v1beta1 import ( "context" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) type VersionHandler struct { diff --git a/server/handler/v1beta1/version_test.go b/server/handler/v1beta1/version_test.go index c579454dbc..5114b62dc3 100644 --- a/server/handler/v1beta1/version_test.go +++ b/server/handler/v1beta1/version_test.go @@ -4,11 +4,11 @@ import ( "context" "testing" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/stretchr/testify/assert" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" - v1 "github.com/odpf/optimus/server/handler/v1beta1" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" + v1 "github.com/raystack/optimus/server/handler/v1beta1" ) func TestVersionHandler(t *testing.T) { diff --git a/server/logger.go b/server/logger.go index a226ab5359..22b839df80 100644 --- a/server/logger.go +++ b/server/logger.go @@ -5,7 +5,7 @@ import ( "io" "os" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" ) type defaultLogger struct { diff --git a/server/optimus.go b/server/optimus.go index af831e4f37..0bca5b6678 100644 --- a/server/optimus.go +++ b/server/optimus.go @@ -13,38 +13,41 @@ import ( "github.com/hashicorp/go-hclog" hPlugin "github.com/hashicorp/go-plugin" "github.com/jackc/pgx/v5/pgxpool" - "github.com/odpf/salt/log" + "github.com/mitchellh/mapstructure" "github.com/prometheus/client_golang/prometheus" + "github.com/raystack/salt/log" slackapi "github.com/slack-go/slack" "google.golang.org/grpc" - "github.com/odpf/optimus/config" - jHandler "github.com/odpf/optimus/core/job/handler/v1beta1" - jResolver "github.com/odpf/optimus/core/job/resolver" - jService "github.com/odpf/optimus/core/job/service" - rModel "github.com/odpf/optimus/core/resource" - rHandler "github.com/odpf/optimus/core/resource/handler/v1beta1" - rService "github.com/odpf/optimus/core/resource/service" - schedulerHandler "github.com/odpf/optimus/core/scheduler/handler/v1beta1" - schedulerResolver "github.com/odpf/optimus/core/scheduler/resolver" - schedulerService "github.com/odpf/optimus/core/scheduler/service" - tHandler "github.com/odpf/optimus/core/tenant/handler/v1beta1" - tService "github.com/odpf/optimus/core/tenant/service" - "github.com/odpf/optimus/ext/notify/pagerduty" - "github.com/odpf/optimus/ext/notify/slack" - bqStore "github.com/odpf/optimus/ext/store/bigquery" - "github.com/odpf/optimus/internal/compiler" - "github.com/odpf/optimus/internal/errors" - "github.com/odpf/optimus/internal/models" - "github.com/odpf/optimus/internal/store/postgres" - jRepo "github.com/odpf/optimus/internal/store/postgres/job" - "github.com/odpf/optimus/internal/store/postgres/resource" - schedulerRepo "github.com/odpf/optimus/internal/store/postgres/scheduler" - "github.com/odpf/optimus/internal/store/postgres/tenant" - "github.com/odpf/optimus/internal/telemetry" - "github.com/odpf/optimus/plugin" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" - oHandler "github.com/odpf/optimus/server/handler/v1beta1" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/core/event/moderator" + jHandler "github.com/raystack/optimus/core/job/handler/v1beta1" + jResolver "github.com/raystack/optimus/core/job/resolver" + jService "github.com/raystack/optimus/core/job/service" + rModel "github.com/raystack/optimus/core/resource" + rHandler "github.com/raystack/optimus/core/resource/handler/v1beta1" + rService "github.com/raystack/optimus/core/resource/service" + schedulerHandler "github.com/raystack/optimus/core/scheduler/handler/v1beta1" + schedulerResolver "github.com/raystack/optimus/core/scheduler/resolver" + schedulerService "github.com/raystack/optimus/core/scheduler/service" + tHandler "github.com/raystack/optimus/core/tenant/handler/v1beta1" + tService "github.com/raystack/optimus/core/tenant/service" + "github.com/raystack/optimus/ext/notify/pagerduty" + "github.com/raystack/optimus/ext/notify/slack" + bqStore "github.com/raystack/optimus/ext/store/bigquery" + "github.com/raystack/optimus/ext/transport/kafka" + "github.com/raystack/optimus/internal/compiler" + "github.com/raystack/optimus/internal/errors" + "github.com/raystack/optimus/internal/models" + "github.com/raystack/optimus/internal/store/postgres" + jRepo "github.com/raystack/optimus/internal/store/postgres/job" + "github.com/raystack/optimus/internal/store/postgres/resource" + schedulerRepo "github.com/raystack/optimus/internal/store/postgres/scheduler" + "github.com/raystack/optimus/internal/store/postgres/tenant" + "github.com/raystack/optimus/internal/telemetry" + "github.com/raystack/optimus/plugin" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" + oHandler "github.com/raystack/optimus/server/handler/v1beta1" ) const keyLength = 32 @@ -64,6 +67,8 @@ type OptimusServer struct { pluginRepo *models.PluginRepository cleanupFn []func() + + eventHandler moderator.Handler } func New(conf *config.ServerConfig) (*OptimusServer, error) { @@ -79,6 +84,7 @@ func New(conf *config.ServerConfig) (*OptimusServer, error) { } setupFns := []setupFn{ + server.setupPublisher, server.setupPlugins, server.setupTelemetry, server.setupAppKey, @@ -101,6 +107,45 @@ func New(conf *config.ServerConfig) (*OptimusServer, error) { return server, nil } +func (s *OptimusServer) setupPublisher() error { + if s.conf.Publisher == nil { + s.eventHandler = moderator.NoOpHandler{} + return nil + } + + ch := make(chan []byte, s.conf.Publisher.Buffer) + + var worker *moderator.Worker + + switch s.conf.Publisher.Type { + case "kafka": + var kafkaConfig config.PublisherKafkaConfig + if err := mapstructure.Decode(s.conf.Publisher.Config, &kafkaConfig); err != nil { + return err + } + + writer := kafka.NewWriter(kafkaConfig.BrokerURLs, kafkaConfig.Topic, s.logger) + interval := time.Second * time.Duration(kafkaConfig.BatchIntervalSecond) + worker = moderator.NewWorker(ch, writer, interval, s.logger) + default: + return fmt.Errorf("publisher with type [%s] is not recognized", s.conf.Publisher.Type) + } + + ctx, cancel := context.WithCancel(context.Background()) + go worker.Run(ctx) + + s.cleanupFn = append(s.cleanupFn, func() { + cancel() + + if err := worker.Close(); err != nil { + s.logger.Error("error closing publishing worker: %v", err) + } + }) + + s.eventHandler = moderator.NewEventHandler(ch, s.logger) + return nil +} + func (s *OptimusServer) setupPlugins() error { pluginLogLevel := hclog.Info if s.conf.Log.Level == config.LogLevelDebug { @@ -235,20 +280,8 @@ func (s *OptimusServer) setupHandlers() error { tProjectService := tService.NewProjectService(tProjectRepo) tNamespaceService := tService.NewNamespaceService(tNamespaceRepo) - tSecretService := tService.NewSecretService(s.key, tSecretRepo) - tenantService := tService.NewTenantService(tProjectService, tNamespaceService, tSecretService) - - // Resource Bounded Context - resourceRepository := resource.NewRepository(s.dbPool) - backupRepository := resource.NewBackupRepository(s.dbPool) - resourceManager := rService.NewResourceManager(resourceRepository, s.logger) - resourceService := rService.NewResourceService(s.logger, resourceRepository, resourceManager, tenantService) - backupService := rService.NewBackupService(backupRepository, resourceRepository, resourceManager) - - // Register datastore - bqClientProvider := bqStore.NewClientProvider() - bigqueryStore := bqStore.NewBigqueryDataStore(tenantService, bqClientProvider) - resourceManager.RegisterDatastore(rModel.Bigquery, bigqueryStore) + tSecretService := tService.NewSecretService(s.key, tSecretRepo, s.logger) + tenantService := tService.NewTenantService(tProjectService, tNamespaceService, tSecretService, s.logger) // Scheduler bounded context jobRunRepo := schedulerRepo.NewJobRunRepository(s.dbPool) @@ -277,9 +310,9 @@ func (s *OptimusServer) setupHandlers() error { newEngine := compiler.NewEngine() - newPriorityResolver := schedulerResolver.NewPriorityResolver() - assetCompiler := schedulerService.NewJobAssetsCompiler(newEngine, s.pluginRepo) - jobInputCompiler := schedulerService.NewJobInputCompiler(tenantService, newEngine, assetCompiler) + newPriorityResolver := schedulerResolver.NewSimpleResolver() + assetCompiler := schedulerService.NewJobAssetsCompiler(newEngine, s.pluginRepo, s.logger) + jobInputCompiler := schedulerService.NewJobInputCompiler(tenantService, newEngine, assetCompiler, s.logger) notificationService := schedulerService.NewNotifyService(s.logger, jobProviderRepo, tenantService, notifierChanels) newScheduler, err := NewScheduler(s.logger, s.conf, s.pluginRepo, tProjectService, tSecretService) if err != nil { @@ -291,10 +324,11 @@ func (s *OptimusServer) setupHandlers() error { replayManager := schedulerService.NewReplayManager(s.logger, replayRepository, replayWorker, func() time.Time { return time.Now().UTC() }, s.conf.Replay) - replayValidator := schedulerService.NewValidator(replayRepository, newScheduler) - replayService := schedulerService.NewReplayService(replayRepository, jobProviderRepo, replayValidator) - newJobRunService := schedulerService.NewJobRunService(s.logger, jobProviderRepo, jobRunRepo, replayRepository, operatorRunRepository, newScheduler, newPriorityResolver, jobInputCompiler) + replayValidator := schedulerService.NewValidator(replayRepository, newScheduler, jobProviderRepo) + replayService := schedulerService.NewReplayService(replayRepository, jobProviderRepo, replayValidator, s.logger) + + newJobRunService := schedulerService.NewJobRunService(s.logger, jobProviderRepo, jobRunRepo, replayRepository, operatorRunRepository, newScheduler, newPriorityResolver, jobInputCompiler, s.eventHandler) // Job Bounded Context Setup jJobRepo := jRepo.NewJobRepository(s.dbPool) @@ -302,7 +336,19 @@ func (s *OptimusServer) setupHandlers() error { jExternalUpstreamResolver, _ := jResolver.NewExternalUpstreamResolver(s.conf.ResourceManagers) jInternalUpstreamResolver := jResolver.NewInternalUpstreamResolver(jJobRepo) jUpstreamResolver := jResolver.NewUpstreamResolver(jJobRepo, jExternalUpstreamResolver, jInternalUpstreamResolver) - jJobService := jService.NewJobService(jJobRepo, jPluginService, jUpstreamResolver, tenantService, s.logger) + jJobService := jService.NewJobService(jJobRepo, jJobRepo, jJobRepo, jPluginService, jUpstreamResolver, tenantService, s.eventHandler, s.logger, newJobRunService, newScheduler) + + // Resource Bounded Context + resourceRepository := resource.NewRepository(s.dbPool) + backupRepository := resource.NewBackupRepository(s.dbPool) + resourceManager := rService.NewResourceManager(resourceRepository, s.logger) + resourceService := rService.NewResourceService(s.logger, resourceRepository, jJobService, resourceManager, s.eventHandler) + backupService := rService.NewBackupService(backupRepository, resourceRepository, resourceManager, s.logger) + + // Register datastore + bqClientProvider := bqStore.NewClientProvider() + bigqueryStore := bqStore.NewBigqueryDataStore(tenantService, bqClientProvider) + resourceManager.RegisterDatastore(rModel.Bigquery, bigqueryStore) // Tenant Handlers pb.RegisterSecretServiceServer(s.grpcServer, tHandler.NewSecretsHandler(s.logger, tSecretService)) diff --git a/server/scheduler.go b/server/scheduler.go index e7cb230937..da165decd6 100644 --- a/server/scheduler.go +++ b/server/scheduler.go @@ -1,12 +1,12 @@ package server import ( - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" - "github.com/odpf/optimus/config" - "github.com/odpf/optimus/ext/scheduler/airflow" - "github.com/odpf/optimus/ext/scheduler/airflow/bucket" - "github.com/odpf/optimus/ext/scheduler/airflow/dag" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/ext/scheduler/airflow" + "github.com/raystack/optimus/ext/scheduler/airflow/bucket" + "github.com/raystack/optimus/ext/scheduler/airflow/dag" ) func NewScheduler(l log.Logger, conf *config.ServerConfig, pluginRepo dag.PluginRepo, projecGetter airflow.ProjectGetter, diff --git a/server/server.go b/server/server.go index a5db1e5002..68fc708c03 100644 --- a/server/server.go +++ b/server/server.go @@ -15,7 +15,7 @@ import ( grpctags "github.com/grpc-ecosystem/go-grpc-middleware/tags" grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" - "github.com/odpf/salt/log" + "github.com/raystack/salt/log" "github.com/sirupsen/logrus" "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" @@ -28,9 +28,9 @@ import ( "google.golang.org/grpc/reflection" "google.golang.org/grpc/status" - "github.com/odpf/optimus/config" - "github.com/odpf/optimus/plugin" - pb "github.com/odpf/optimus/protos/odpf/optimus/core/v1beta1" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/plugin" + pb "github.com/raystack/optimus/protos/raystack/optimus/core/v1beta1" ) const ( diff --git a/tests/bench/job/job_repo_test.go b/tests/bench/job/job_repo_test.go index 9c84e467e4..885ad6d9da 100644 --- a/tests/bench/job/job_repo_test.go +++ b/tests/bench/job/job_repo_test.go @@ -10,11 +10,11 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/stretchr/testify/assert" - serviceJob "github.com/odpf/optimus/core/job" - serviceTenant "github.com/odpf/optimus/core/tenant" - repoJob "github.com/odpf/optimus/internal/store/postgres/job" - repoTenant "github.com/odpf/optimus/internal/store/postgres/tenant" - "github.com/odpf/optimus/tests/setup" + serviceJob "github.com/raystack/optimus/core/job" + serviceTenant "github.com/raystack/optimus/core/tenant" + repoJob "github.com/raystack/optimus/internal/store/postgres/job" + repoTenant "github.com/raystack/optimus/internal/store/postgres/tenant" + "github.com/raystack/optimus/tests/setup" ) func BenchmarkJobRepository(b *testing.B) { diff --git a/tests/bench/resource/backup_repo_test.go b/tests/bench/resource/backup_repo_test.go index 9f13d1e4ac..92033bee51 100644 --- a/tests/bench/resource/backup_repo_test.go +++ b/tests/bench/resource/backup_repo_test.go @@ -10,11 +10,11 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/stretchr/testify/assert" - serviceResource "github.com/odpf/optimus/core/resource" - serviceTenant "github.com/odpf/optimus/core/tenant" - repoResource "github.com/odpf/optimus/internal/store/postgres/resource" - repoTenant "github.com/odpf/optimus/internal/store/postgres/tenant" - "github.com/odpf/optimus/tests/setup" + serviceResource "github.com/raystack/optimus/core/resource" + serviceTenant "github.com/raystack/optimus/core/tenant" + repoResource "github.com/raystack/optimus/internal/store/postgres/resource" + repoTenant "github.com/raystack/optimus/internal/store/postgres/tenant" + "github.com/raystack/optimus/tests/setup" ) func BenchmarkBackupRepository(b *testing.B) { diff --git a/tests/bench/resource/resource_repo_test.go b/tests/bench/resource/resource_repo_test.go index bfe2591daa..9f1bddb051 100644 --- a/tests/bench/resource/resource_repo_test.go +++ b/tests/bench/resource/resource_repo_test.go @@ -10,12 +10,12 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/stretchr/testify/assert" - serviceResource "github.com/odpf/optimus/core/resource" - serviceTenant "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/ext/store/bigquery" - repoResource "github.com/odpf/optimus/internal/store/postgres/resource" - repoTenant "github.com/odpf/optimus/internal/store/postgres/tenant" - "github.com/odpf/optimus/tests/setup" + serviceResource "github.com/raystack/optimus/core/resource" + serviceTenant "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/ext/store/bigquery" + repoResource "github.com/raystack/optimus/internal/store/postgres/resource" + repoTenant "github.com/raystack/optimus/internal/store/postgres/tenant" + "github.com/raystack/optimus/tests/setup" ) func BenchmarkResourceRepository(b *testing.B) { diff --git a/tests/bench/scheduler/job_repo_test.go b/tests/bench/scheduler/job_repo_test.go index ce7776c87b..805da3b06a 100644 --- a/tests/bench/scheduler/job_repo_test.go +++ b/tests/bench/scheduler/job_repo_test.go @@ -10,13 +10,13 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/stretchr/testify/assert" - serviceJob "github.com/odpf/optimus/core/job" - serviceScheduler "github.com/odpf/optimus/core/scheduler" - serviceTenant "github.com/odpf/optimus/core/tenant" - repoJob "github.com/odpf/optimus/internal/store/postgres/job" - repoScheduler "github.com/odpf/optimus/internal/store/postgres/scheduler" - repoTenant "github.com/odpf/optimus/internal/store/postgres/tenant" - "github.com/odpf/optimus/tests/setup" + serviceJob "github.com/raystack/optimus/core/job" + serviceScheduler "github.com/raystack/optimus/core/scheduler" + serviceTenant "github.com/raystack/optimus/core/tenant" + repoJob "github.com/raystack/optimus/internal/store/postgres/job" + repoScheduler "github.com/raystack/optimus/internal/store/postgres/scheduler" + repoTenant "github.com/raystack/optimus/internal/store/postgres/tenant" + "github.com/raystack/optimus/tests/setup" ) func BenchmarkJobRepository(b *testing.B) { diff --git a/tests/bench/scheduler/job_run_repo_test.go b/tests/bench/scheduler/job_run_repo_test.go index 3e1a0969e6..d47ceb5908 100644 --- a/tests/bench/scheduler/job_run_repo_test.go +++ b/tests/bench/scheduler/job_run_repo_test.go @@ -11,13 +11,13 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/stretchr/testify/assert" - serviceJob "github.com/odpf/optimus/core/job" - serviceScheduler "github.com/odpf/optimus/core/scheduler" - serviceTenant "github.com/odpf/optimus/core/tenant" - repoJob "github.com/odpf/optimus/internal/store/postgres/job" - repoScheduler "github.com/odpf/optimus/internal/store/postgres/scheduler" - repoTenant "github.com/odpf/optimus/internal/store/postgres/tenant" - "github.com/odpf/optimus/tests/setup" + serviceJob "github.com/raystack/optimus/core/job" + serviceScheduler "github.com/raystack/optimus/core/scheduler" + serviceTenant "github.com/raystack/optimus/core/tenant" + repoJob "github.com/raystack/optimus/internal/store/postgres/job" + repoScheduler "github.com/raystack/optimus/internal/store/postgres/scheduler" + repoTenant "github.com/raystack/optimus/internal/store/postgres/tenant" + "github.com/raystack/optimus/tests/setup" ) func BenchmarkJobRunRepository(b *testing.B) { diff --git a/tests/bench/scheduler/operator_run_repo_test.go b/tests/bench/scheduler/operator_run_repo_test.go index 4662dc9323..29e0bb6f77 100644 --- a/tests/bench/scheduler/operator_run_repo_test.go +++ b/tests/bench/scheduler/operator_run_repo_test.go @@ -11,13 +11,13 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/stretchr/testify/assert" - serviceJob "github.com/odpf/optimus/core/job" - serviceScheduler "github.com/odpf/optimus/core/scheduler" - serviceTenant "github.com/odpf/optimus/core/tenant" - repoJob "github.com/odpf/optimus/internal/store/postgres/job" - repoScheduler "github.com/odpf/optimus/internal/store/postgres/scheduler" - repoTenant "github.com/odpf/optimus/internal/store/postgres/tenant" - "github.com/odpf/optimus/tests/setup" + serviceJob "github.com/raystack/optimus/core/job" + serviceScheduler "github.com/raystack/optimus/core/scheduler" + serviceTenant "github.com/raystack/optimus/core/tenant" + repoJob "github.com/raystack/optimus/internal/store/postgres/job" + repoScheduler "github.com/raystack/optimus/internal/store/postgres/scheduler" + repoTenant "github.com/raystack/optimus/internal/store/postgres/tenant" + "github.com/raystack/optimus/tests/setup" ) func BenchmarkOperatorRunRepository(b *testing.B) { diff --git a/tests/bench/tenant/namespace_repo_test.go b/tests/bench/tenant/namespace_repo_test.go index 949dc489d3..6a148b264b 100644 --- a/tests/bench/tenant/namespace_repo_test.go +++ b/tests/bench/tenant/namespace_repo_test.go @@ -10,9 +10,9 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/stretchr/testify/assert" - serviceTenant "github.com/odpf/optimus/core/tenant" - repoTenant "github.com/odpf/optimus/internal/store/postgres/tenant" - "github.com/odpf/optimus/tests/setup" + serviceTenant "github.com/raystack/optimus/core/tenant" + repoTenant "github.com/raystack/optimus/internal/store/postgres/tenant" + "github.com/raystack/optimus/tests/setup" ) func BenchmarkNamespaceRepository(b *testing.B) { diff --git a/tests/bench/tenant/project_repo_test.go b/tests/bench/tenant/project_repo_test.go index cdd15c2f51..71939cb68c 100644 --- a/tests/bench/tenant/project_repo_test.go +++ b/tests/bench/tenant/project_repo_test.go @@ -10,9 +10,9 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/stretchr/testify/assert" - serviceTenant "github.com/odpf/optimus/core/tenant" - repoTenant "github.com/odpf/optimus/internal/store/postgres/tenant" - "github.com/odpf/optimus/tests/setup" + serviceTenant "github.com/raystack/optimus/core/tenant" + repoTenant "github.com/raystack/optimus/internal/store/postgres/tenant" + "github.com/raystack/optimus/tests/setup" ) func BenchmarkProjectRepository(b *testing.B) { diff --git a/tests/bench/tenant/secret_repo_test.go b/tests/bench/tenant/secret_repo_test.go index fdc59a3f02..f1e88960b1 100644 --- a/tests/bench/tenant/secret_repo_test.go +++ b/tests/bench/tenant/secret_repo_test.go @@ -10,9 +10,9 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/stretchr/testify/assert" - serviceTenant "github.com/odpf/optimus/core/tenant" - repoTenant "github.com/odpf/optimus/internal/store/postgres/tenant" - "github.com/odpf/optimus/tests/setup" + serviceTenant "github.com/raystack/optimus/core/tenant" + repoTenant "github.com/raystack/optimus/internal/store/postgres/tenant" + "github.com/raystack/optimus/tests/setup" ) func BenchmarkSecretRepository(b *testing.B) { diff --git a/tests/setup/database.go b/tests/setup/database.go index daaf5d4a13..d909215f04 100644 --- a/tests/setup/database.go +++ b/tests/setup/database.go @@ -11,8 +11,8 @@ import ( "github.com/golang-migrate/migrate/v4" "github.com/jackc/pgx/v5/pgxpool" - "github.com/odpf/optimus/config" - "github.com/odpf/optimus/internal/store/postgres" + "github.com/raystack/optimus/config" + "github.com/raystack/optimus/internal/store/postgres" ) var ( diff --git a/tests/setup/job.go b/tests/setup/job.go index 8e164f85f9..2cb6125127 100644 --- a/tests/setup/job.go +++ b/tests/setup/job.go @@ -1,9 +1,9 @@ package setup import ( - "github.com/odpf/optimus/core/job" - "github.com/odpf/optimus/core/tenant" - "github.com/odpf/optimus/internal/models" + "github.com/raystack/optimus/core/job" + "github.com/raystack/optimus/core/tenant" + "github.com/raystack/optimus/internal/models" ) type DummyJobBuilder struct { diff --git a/tests/setup/plugin.go b/tests/setup/plugin.go index 56ee6edcd7..d54b1becb6 100644 --- a/tests/setup/plugin.go +++ b/tests/setup/plugin.go @@ -5,7 +5,7 @@ import ( "errors" "fmt" - "github.com/odpf/optimus/sdk/plugin" + "github.com/raystack/optimus/sdk/plugin" ) type MockPluginBQ struct{}

sIEd1;8w*M89TJVsxd+bo(&Y7>-GW4|r?27Cp>L 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 diff --git a/docs/static/img/docs/OptimusIntro.png b/docs/static/img/docs/OptimusIntro.png new file mode 100644 index 0000000000000000000000000000000000000000..d5fd590b59cbf24d60bf0ba67585aab8272abc22 GIT binary patch literal 92067 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 z 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 z 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 _Ms#EB4laZgZVwrOQ%Q? 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