From b4482c33dfbcf891685aacef8c2c7fb3572acab5 Mon Sep 17 00:00:00 2001 From: Eitan Yarmush Date: Thu, 21 Feb 2019 14:16:25 -0500 Subject: [PATCH] moved everything and got dep to pass (#14) * moved everything and got dep to pass * added very basic build script * protos are broken * protos generating correctly * build works except for docs * remove unecessary install scripts * got docs pushing works * renamed chart * basic CLI * added basic schema functionality * helm manifest works properly now * event loop is working * added kube install * install command works * basic feature parity with the CLI * started basic example, and moved schema * can actually start playing with queries now * upgraded gloo * finished basic tutorial * example really works now * spelling mistake * cleaned up protos from PR * removed commented code besides tests * created CLI tests * all old unit tests are passing * midway through docs work * creating cli docs * added cli-gen to code-gen * upped gloo version to 0.7.6 * thanks marco, can now install into any namespace :) * updated code-gen * more checking on install command * made docs more closely match docs repo * getting started on kubernetes doc is now accurate * updated examples * removed docs from local dir * was in wrong dir * added docs gen to release build * removed unused code from tests --- .firebaserc | 14 - .githooks/pre-commit | 30 + .gitignore | 7 + Dockerfile.site | 2 - Gopkg.lock | 1483 ++++++++++++++--- Gopkg.toml | 97 +- Makefile | 291 ++-- api/v1/config.proto | 17 - api/v1/resolver_map.proto | 76 +- api/v1/schema.proto | 33 +- api/v1/solo-kit.json | 17 + ci/check-code-and-docs-gen.sh | 44 + ci/push-docs.sh | 72 + ci/spell.sh | 93 ++ {hack => ci}/upload-github-release-asset.sh | 13 +- cli/cmd/docs/main.go | 67 + cli/cmd/main.go | 24 + cli/pkg/cmd/install/kube.go | 30 + cli/pkg/cmd/install/root.go | 40 + cli/pkg/cmd/install/util.go | 192 +++ cli/pkg/cmd/resolvermap/resolvermap_delete.go | 42 + .../cmd/resolvermap/resolvermap_register.go | 124 ++ .../cmd/resolvermap/resolvermap_suite_test.go | 6 +- cli/pkg/cmd/resolvermap/resolvermap_test.go | 195 +++ cli/pkg/cmd/resolvermap/root.go | 23 + cli/pkg/cmd/resolvermap/util.go | 31 + cli/pkg/cmd/root.go | 47 + cli/pkg/cmd/schema/root.go | 27 + cli/pkg/cmd/schema/schema_create.go | 56 + cli/pkg/cmd/schema/schema_delete.go | 42 + .../pkg/cmd/schema/schema_suite_test.go | 6 +- cli/pkg/cmd/schema/schema_update.go | 60 + cli/pkg/cmd/schema/shema_test.go | 130 ++ cli/pkg/common/main.go | 25 + cli/pkg/constants/constants.go | 17 + cli/pkg/flagutils/common.go | 24 + cli/pkg/flagutils/install.go | 17 + cli/pkg/flagutils/metadata.go | 19 + cli/pkg/flagutils/resolver_map.go | 16 + cli/pkg/helpers/clients.go | 144 ++ cli/pkg/options/types.go | 44 + cli/pkg/testutils/main.go | 78 + cloudbuild-cache.yaml | 15 + cloudbuild.yaml | 109 ++ cmd/Dockerfile | 9 + cmd/main.go | 22 + cmd/sqoop/Dockerfile | 4 - cmd/sqoop/Dockerfile.debug | 4 - cmd/sqoop/main.go | 49 - cmd/sqoopctl/main.go | 18 - docs/QLoo.png | Bin 107552 -> 0 bytes docs/Sqoop.png | Bin 62883 -> 0 bytes docs/{index.md => _index.md} | 13 +- docs/api.json | 635 ------- docs/cli/_index.md | 4 + docs/cli/sqoopctl.md | 36 + docs/cli/sqoopctl_install.md | 31 + docs/cli/sqoopctl_install_kube.md | 37 + docs/cli/sqoopctl_resolvermap.md | 32 + docs/cli/sqoopctl_resolvermap_register.md | 41 + docs/cli/sqoopctl_resolvermap_reset.md | 34 + docs/cli/sqoopctl_schema.md | 35 + docs/cli/sqoopctl_schema_create.md | 36 + docs/cli/sqoopctl_schema_delete.md | 36 + docs/cli/sqoopctl_schema_update.md | 36 + docs/cli/sqoopctl_uninstall.md | 34 + docs/extra.css | 3 - docs/gen_docs.go | 190 --- docs/getting_started/docker/1.md | 263 --- docs/getting_started/kubernetes/1.md | 226 --- docs/img/Sqoop.png | Bin 62883 -> 0 bytes docs/img/favicon.ico | Bin 1150 -> 0 bytes docs/installation/docker.md | 82 - docs/installation/kubernetes.md | 72 - docs/introduction/architecture.md | 25 - docs/introduction/concepts/api_objects.md | 85 - docs/introduction/concepts/resolvers.md | 65 - docs/introduction/high_level_architecture.png | Bin 423757 -> 0 bytes docs/introduction/introduction.md | 29 - docs/introduction/low_level_arch.png | Bin 238227 -> 0 bytes docs/markdown.tmpl | 69 - docs/v1/config.md | 39 - .../gloo/api/v1/extensions.proto.sk.md | 63 + .../projects/gloo/api/v1/plugins.proto.sk.md | 156 ++ .../gloo/api/v1/plugins/aws/aws.proto.sk.md | 114 ++ .../api/v1/plugins/azure/azure.proto.sk.md | 107 ++ .../api/v1/plugins/consul/consul.proto.sk.md | 56 + .../plugins/faultinjection/fault.proto.sk.md | 93 ++ .../gloo/api/v1/plugins/grpc/grpc.proto.sk.md | 99 ++ .../plugins/kubernetes/kubernetes.proto.sk.md | 58 + .../gloo/api/v1/plugins/rest/rest.proto.sk.md | 90 + .../v1/plugins/retries/retries.proto.sk.md | 56 + .../api/v1/plugins/service_spec.proto.sk.md | 51 + .../api/v1/plugins/static/static.proto.sk.md | 76 + .../transformation/parameters.proto.sk.md | 47 + .../transformation/prefix_rewrite.proto.sk.md | 52 + .../transformation/transformation.proto.sk.md | 193 +++ .../projects/gloo/api/v1/proxy.proto.sk.md | 402 +++++ .../gloo/projects/gloo/api/v1/ssl.proto.sk.md | 96 ++ .../solo-kit/api/v1/metadata.proto.sk.md | 55 + .../solo-io/solo-kit/api/v1/ref.proto.sk.md | 49 + .../solo-kit/api/v1/solo-kit.proto.sk.md | 51 + .../solo-kit/api/v1/status.proto.sk.md | 69 + .../sqoop/api/v1/resolver_map.proto.sk.md | 201 +++ .../solo-io/sqoop/api/v1/schema.proto.sk.md | 53 + docs/v1/gogoproto/gogo.proto.sk.md | 51 + .../v1/google/protobuf/descriptor.proto.sk.md | 832 +++++++++ docs/v1/google/protobuf/duration.proto.sk.md | 137 ++ docs/v1/google/protobuf/struct.proto.sk.md | 158 ++ docs/v1/google/protobuf/timestamp.proto.sk.md | 155 ++ docs/v1/google/protobuf/wrappers.proto.sk.md | 252 +++ docs/v1/resolver_map.md | 225 --- docs/v1/schema.md | 46 - docs/v1/sqoop.solo.io.project.sk.md | 24 + examples/petstore/README.md | 108 +- examples/petstore/demo.sh | 15 - examples/petstore/petstore.schema.graphql | 8 +- examples/petstore/petstore.yaml | 41 + examples/starwars/Dockerfile | 4 - examples/starwars/Makefile | 18 - examples/starwars/envoy-config.yaml | 28 - examples/starwars/server/server.go | 83 - .../resolver_maps/starwars_resolvers.yaml | 43 - .../sqoop-config/schemas/starwars_schema.yaml | 134 -- examples/starwars/starwars-kubernetes.yaml | 290 ---- examples/starwars/starwars-rest.go | 15 - examples/starwars/starwars.schema.graphql | 131 -- examples/starwars/upstream.yaml | 26 - firebase.json | 11 - generate.go | 24 + hack/create-release.sh | 38 - hack/generate_protos.sh | 65 + install/.gitignore | 8 + install/docker-compose/docker-compose.yaml | 54 - install/docker-compose/envoy-config.yaml | 28 - .../prepare-config-directories.sh | 48 - install/helm/sqoop/.helmignore | 27 + install/helm/sqoop/Chart-template.yaml | 4 + install/helm/sqoop/generate.go | 166 ++ install/helm/sqoop/generate/requirements.go | 11 + install/helm/sqoop/generate/values.go | 42 + install/helm/sqoop/requirements-template.yaml | 3 + .../helm/sqoop/templates/0-sqoop-service.yaml | 16 + .../sqoop/templates/1-sqoop-deployment.yaml | 53 + .../sqoop/templates/2-sqoop-clusterrole.yaml | 9 + .../3-sqoop-clusterrole-binding.yaml | 14 + .../templates/4-sqoop-envoy-configmap.yaml | 46 + install/helm/sqoop/values-template.yaml | 22 + install/kube/README.md | 41 - install/kube/install.yaml | 434 ----- mkdocs.yml | 38 - pin_repos.go | 27 + pkg/api/types/v1/config.pb.go | 127 -- pkg/api/types/v1/interface.go | 36 - pkg/api/types/v1/resolver_map.pb.go | 957 ----------- pkg/api/types/v1/schema.pb.go | 139 -- pkg/api/v1/api_event_loop.sk.go | 96 ++ pkg/api/v1/api_event_loop_test.go | 71 + pkg/api/v1/api_snapshot.sk.go | 43 + pkg/api/v1/api_snapshot_emitter.sk.go | 209 +++ pkg/api/v1/api_snapshot_emitter_test.go | 343 ++++ pkg/api/v1/resolver_map.pb.go | 967 +++++++++++ pkg/api/v1/resolver_map.sk.go | 167 ++ pkg/api/v1/resolver_map_client.sk.go | 118 ++ pkg/api/v1/resolver_map_client_test.go | 181 ++ pkg/api/v1/resolver_map_reconciler.sk.go | 47 + pkg/api/v1/schema.pb.go | 153 ++ pkg/api/v1/schema.sk.go | 167 ++ pkg/api/v1/schema_client.sk.go | 118 ++ pkg/api/v1/schema_client_test.go | 181 ++ pkg/api/v1/schema_reconciler.sk.go | 47 + pkg/api/v1/sqoop.solo.io_suite_test.go | 15 + pkg/bootstrap/bootstrap.go | 52 - pkg/bootstrap/flags/sqoop_flags.go | 18 - pkg/configwatcher/config_watcher.go | 136 -- pkg/configwatcher/configwatcher_suite_test.go | 15 - pkg/configwatcher/file_config_watcher_test.go | 70 - pkg/configwatcher/interface.go | 9 - pkg/core/core_suite_test.go | 76 - pkg/core/core_test.go | 169 -- pkg/core/event_loop.go | 239 --- pkg/defaults/main.go | 5 + pkg/{ => engine}/dynamic/values.go | 0 pkg/{ => engine}/exec/executable_resolvers.go | 6 +- pkg/{ => engine}/exec/executable_schema.go | 2 +- pkg/engine/graphql_engine.go | 53 + pkg/{ => engine}/resolvers/factory.go | 28 +- .../resolvers/gloo/gloo_resolvers.go | 56 +- .../resolvers/node/node_resolvers.go | 4 +- .../resolvers/template/template_resolvers.go | 9 +- pkg/engine/router/landing_page.go | 52 + pkg/{graphql => engine/router}/router.go | 13 +- pkg/{ => engine}/util/template_utils.go | 2 +- pkg/graphql/graphql_suite_test.go | 13 - pkg/graphql/landing_page.go | 147 -- pkg/operator/gloo_operator.go | 192 --- pkg/operator/gloo_operator_test.go | 160 -- pkg/operator/gloo_routes.go | 55 - pkg/reporter/crd_reporter_test.go | 154 -- pkg/reporter/interface.go | 14 - pkg/reporter/reporter.go | 68 - pkg/reporter/reporter_suite_test.go | 16 - pkg/resolvers/gloo/gloo_suite_test.go | 13 - pkg/resolvers/template/template_suite_test.go | 13 - pkg/setup/setup.go | 22 + pkg/sqoopctl/install/docker.go | 160 -- pkg/sqoopctl/install/kube.go | 52 - pkg/sqoopctl/install/root.go | 16 - .../resolvermap/resolvermap_create.go | 55 - .../resolvermap/resolvermap_delete.go | 37 - pkg/sqoopctl/resolvermap/resolvermap_get.go | 51 - .../resolvermap/resolvermap_register.go | 106 -- .../resolvermap/resolvermap_update.go | 68 - pkg/sqoopctl/resolvermap/root.go | 19 - pkg/sqoopctl/root.go | 102 -- pkg/sqoopctl/schema/root.go | 19 - pkg/sqoopctl/schema/schema_create.go | 64 - pkg/sqoopctl/schema/schema_delete.go | 37 - pkg/sqoopctl/schema/schema_get.go | 51 - pkg/sqoopctl/schema/schema_update.go | 71 - pkg/storage/README.md | 10 - pkg/storage/base/base_client.go | 185 -- pkg/storage/base/conversion.go | 61 - pkg/storage/base/storable_item.go | 92 - pkg/storage/consul/client_template.go.tmpl | 67 - pkg/storage/consul/consul_storage_client.go | 58 - .../consul/consul_storage_client_test.go | 377 ----- pkg/storage/consul/consul_suite_test.go | 42 - pkg/storage/consul/resolver_maps.go | 67 - pkg/storage/consul/schemas.go | 67 - .../client/clientset/versioned/clientset.go | 98 -- .../crd/client/clientset/versioned/doc.go | 20 - .../versioned/fake/clientset_generated.go | 82 - .../client/clientset/versioned/fake/doc.go | 20 - .../clientset/versioned/fake/register.go | 54 - .../client/clientset/versioned/scheme/doc.go | 20 - .../clientset/versioned/scheme/register.go | 54 - .../versioned/typed/solo.io/v1/doc.go | 20 - .../versioned/typed/solo.io/v1/fake/doc.go | 20 - .../typed/solo.io/v1/fake/fake_resolvermap.go | 128 -- .../typed/solo.io/v1/fake/fake_schema.go | 128 -- .../solo.io/v1/fake/fake_solo.io_client.go | 44 - .../typed/solo.io/v1/generated_expansion.go | 23 - .../versioned/typed/solo.io/v1/resolvermap.go | 157 -- .../versioned/typed/solo.io/v1/schema.go | 157 -- .../typed/solo.io/v1/solo.io_client.go | 95 -- .../informers/externalversions/factory.go | 180 -- .../informers/externalversions/generic.go | 64 - .../internalinterfaces/factory_interfaces.go | 38 - .../externalversions/solo.io/interface.go | 46 - .../externalversions/solo.io/v1/interface.go | 52 - .../solo.io/v1/resolvermap.go | 89 - .../externalversions/solo.io/v1/schema.go | 89 - .../listers/solo.io/v1/expansion_generated.go | 35 - .../client/listers/solo.io/v1/resolvermap.go | 94 -- .../crd/client/listers/solo.io/v1/schema.go | 94 -- pkg/storage/crd/client_template.go.tmpl | 193 --- pkg/storage/crd/conversion.go | 101 -- pkg/storage/crd/conversion_test.go | 57 - pkg/storage/crd/crd_storage_client.go | 112 -- pkg/storage/crd/crd_storage_client_test.go | 246 --- pkg/storage/crd/crd_suite_test.go | 15 - pkg/storage/crd/resolver_maps.go | 193 --- pkg/storage/crd/schemas.go | 193 --- pkg/storage/crd/solo.io/v1/doc.go | 21 - pkg/storage/crd/solo.io/v1/register.go | 75 - pkg/storage/crd/solo.io/v1/types.go | 73 - .../crd/solo.io/v1/zz_generated.deepcopy.go | 191 --- pkg/storage/crud/crud.go | 8 - pkg/storage/errors.go | 25 - pkg/storage/file/client_template.go.tmpl | 248 --- pkg/storage/file/directory.go | 3 - pkg/storage/file/file_io.go | 31 - pkg/storage/file/file_storage_client.go | 65 - pkg/storage/file/file_storage_client_test.go | 191 --- pkg/storage/file/file_suite_test.go | 15 - pkg/storage/file/resolver_maps.go | 248 --- pkg/storage/file/resource_ver.go | 18 - pkg/storage/file/schemas.go | 248 --- pkg/storage/generate/generate_clients.go | 71 - pkg/storage/interface.go | 32 - pkg/storage/watcher.go | 87 - pkg/syncer/graphql_syncer.go | 150 ++ pkg/syncer/setup_syncer.go | 185 ++ pkg/todo/todos.go | 22 + .../generate_resolver_map.go | 18 +- pkg/translator/routes.go | 51 + pkg/translator/translator.go | 60 + test/kube_e2e/helpers.go | 152 -- .../kube_e2e/kube_resources/install.tmpl.yaml | 434 ----- .../testing-resources.tmpl.yaml | 304 ---- test/kube_e2e/kubernetes_e2e_test.go | 198 --- test/kube_e2e/kubernetes_happy_path_test.go | 20 - test/kube_e2e/kubernetes_suite_test.go | 45 - test/{ => testdata}/helpers.go | 8 +- .../testdata}/starwars/generated.go | 0 .../testdata}/starwars/model.go | 0 .../testdata}/starwars/models_gen.go | 0 .../testdata}/starwars/resolvers.go | 0 test/{ => testdata}/utils.go | 132 +- .../unit}/executable_resolvers_test.go | 23 +- .../gloo => test/unit}/gloo_resolvers_test.go | 36 +- .../dynamic => test/unit}/ordered_map_test.go | 14 +- {pkg/graphql => test/unit}/router_test.go | 24 +- .../unit/sqoop_unit_suite_test.go | 4 +- .../unit}/template_resolvers_test.go | 15 +- version | 1 - version/check.go | 46 + version/linked.go | 10 + 309 files changed, 12291 insertions(+), 14892 deletions(-) delete mode 100644 .firebaserc create mode 100755 .githooks/pre-commit delete mode 100644 Dockerfile.site delete mode 100644 api/v1/config.proto create mode 100644 api/v1/solo-kit.json create mode 100755 ci/check-code-and-docs-gen.sh create mode 100755 ci/push-docs.sh create mode 100755 ci/spell.sh rename {hack => ci}/upload-github-release-asset.sh (79%) create mode 100644 cli/cmd/docs/main.go create mode 100644 cli/cmd/main.go create mode 100644 cli/pkg/cmd/install/kube.go create mode 100644 cli/pkg/cmd/install/root.go create mode 100644 cli/pkg/cmd/install/util.go create mode 100644 cli/pkg/cmd/resolvermap/resolvermap_delete.go create mode 100644 cli/pkg/cmd/resolvermap/resolvermap_register.go rename pkg/operator/gloo_suite_test.go => cli/pkg/cmd/resolvermap/resolvermap_suite_test.go (53%) create mode 100644 cli/pkg/cmd/resolvermap/resolvermap_test.go create mode 100644 cli/pkg/cmd/resolvermap/root.go create mode 100644 cli/pkg/cmd/resolvermap/util.go create mode 100644 cli/pkg/cmd/root.go create mode 100644 cli/pkg/cmd/schema/root.go create mode 100644 cli/pkg/cmd/schema/schema_create.go create mode 100644 cli/pkg/cmd/schema/schema_delete.go rename pkg/exec/exec_suite_test.go => cli/pkg/cmd/schema/schema_suite_test.go (57%) create mode 100644 cli/pkg/cmd/schema/schema_update.go create mode 100644 cli/pkg/cmd/schema/shema_test.go create mode 100644 cli/pkg/common/main.go create mode 100644 cli/pkg/constants/constants.go create mode 100644 cli/pkg/flagutils/common.go create mode 100644 cli/pkg/flagutils/install.go create mode 100644 cli/pkg/flagutils/metadata.go create mode 100644 cli/pkg/flagutils/resolver_map.go create mode 100644 cli/pkg/helpers/clients.go create mode 100644 cli/pkg/options/types.go create mode 100644 cli/pkg/testutils/main.go create mode 100644 cloudbuild-cache.yaml create mode 100644 cloudbuild.yaml create mode 100644 cmd/Dockerfile create mode 100644 cmd/main.go delete mode 100644 cmd/sqoop/Dockerfile delete mode 100644 cmd/sqoop/Dockerfile.debug delete mode 100644 cmd/sqoop/main.go delete mode 100644 cmd/sqoopctl/main.go delete mode 100644 docs/QLoo.png delete mode 100644 docs/Sqoop.png rename docs/{index.md => _index.md} (96%) delete mode 100644 docs/api.json create mode 100644 docs/cli/_index.md create mode 100644 docs/cli/sqoopctl.md create mode 100644 docs/cli/sqoopctl_install.md create mode 100644 docs/cli/sqoopctl_install_kube.md create mode 100644 docs/cli/sqoopctl_resolvermap.md create mode 100644 docs/cli/sqoopctl_resolvermap_register.md create mode 100644 docs/cli/sqoopctl_resolvermap_reset.md create mode 100644 docs/cli/sqoopctl_schema.md create mode 100644 docs/cli/sqoopctl_schema_create.md create mode 100644 docs/cli/sqoopctl_schema_delete.md create mode 100644 docs/cli/sqoopctl_schema_update.md create mode 100644 docs/cli/sqoopctl_uninstall.md delete mode 100644 docs/extra.css delete mode 100644 docs/gen_docs.go delete mode 100644 docs/getting_started/docker/1.md delete mode 100644 docs/getting_started/kubernetes/1.md delete mode 100644 docs/img/Sqoop.png delete mode 100644 docs/img/favicon.ico delete mode 100644 docs/installation/docker.md delete mode 100644 docs/installation/kubernetes.md delete mode 100644 docs/introduction/architecture.md delete mode 100644 docs/introduction/concepts/api_objects.md delete mode 100644 docs/introduction/concepts/resolvers.md delete mode 100644 docs/introduction/high_level_architecture.png delete mode 100644 docs/introduction/introduction.md delete mode 100644 docs/introduction/low_level_arch.png delete mode 100644 docs/markdown.tmpl delete mode 100644 docs/v1/config.md create mode 100644 docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/extensions.proto.sk.md create mode 100644 docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins.proto.sk.md create mode 100644 docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/aws/aws.proto.sk.md create mode 100644 docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/azure/azure.proto.sk.md create mode 100644 docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/consul/consul.proto.sk.md create mode 100644 docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/faultinjection/fault.proto.sk.md create mode 100644 docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/grpc/grpc.proto.sk.md create mode 100644 docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/kubernetes/kubernetes.proto.sk.md create mode 100644 docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/rest/rest.proto.sk.md create mode 100644 docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/retries/retries.proto.sk.md create mode 100644 docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/service_spec.proto.sk.md create mode 100644 docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/static/static.proto.sk.md create mode 100644 docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/transformation/parameters.proto.sk.md create mode 100644 docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/transformation/prefix_rewrite.proto.sk.md create mode 100644 docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/transformation/transformation.proto.sk.md create mode 100644 docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/proxy.proto.sk.md create mode 100644 docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/ssl.proto.sk.md create mode 100644 docs/v1/github.com/solo-io/solo-kit/api/v1/metadata.proto.sk.md create mode 100644 docs/v1/github.com/solo-io/solo-kit/api/v1/ref.proto.sk.md create mode 100644 docs/v1/github.com/solo-io/solo-kit/api/v1/solo-kit.proto.sk.md create mode 100644 docs/v1/github.com/solo-io/solo-kit/api/v1/status.proto.sk.md create mode 100644 docs/v1/github.com/solo-io/sqoop/api/v1/resolver_map.proto.sk.md create mode 100644 docs/v1/github.com/solo-io/sqoop/api/v1/schema.proto.sk.md create mode 100644 docs/v1/gogoproto/gogo.proto.sk.md create mode 100644 docs/v1/google/protobuf/descriptor.proto.sk.md create mode 100644 docs/v1/google/protobuf/duration.proto.sk.md create mode 100644 docs/v1/google/protobuf/struct.proto.sk.md create mode 100644 docs/v1/google/protobuf/timestamp.proto.sk.md create mode 100644 docs/v1/google/protobuf/wrappers.proto.sk.md delete mode 100644 docs/v1/resolver_map.md delete mode 100644 docs/v1/schema.md create mode 100644 docs/v1/sqoop.solo.io.project.sk.md delete mode 100644 examples/petstore/demo.sh create mode 100644 examples/petstore/petstore.yaml delete mode 100644 examples/starwars/Dockerfile delete mode 100644 examples/starwars/Makefile delete mode 100644 examples/starwars/envoy-config.yaml delete mode 100644 examples/starwars/server/server.go delete mode 100644 examples/starwars/sqoop-config/resolver_maps/starwars_resolvers.yaml delete mode 100644 examples/starwars/sqoop-config/schemas/starwars_schema.yaml delete mode 100644 examples/starwars/starwars-kubernetes.yaml delete mode 100644 examples/starwars/starwars-rest.go delete mode 100644 examples/starwars/starwars.schema.graphql delete mode 100644 examples/starwars/upstream.yaml delete mode 100644 firebase.json create mode 100644 generate.go delete mode 100755 hack/create-release.sh create mode 100755 hack/generate_protos.sh create mode 100644 install/.gitignore delete mode 100644 install/docker-compose/docker-compose.yaml delete mode 100644 install/docker-compose/envoy-config.yaml delete mode 100755 install/docker-compose/prepare-config-directories.sh create mode 100644 install/helm/sqoop/.helmignore create mode 100644 install/helm/sqoop/Chart-template.yaml create mode 100644 install/helm/sqoop/generate.go create mode 100644 install/helm/sqoop/generate/requirements.go create mode 100644 install/helm/sqoop/generate/values.go create mode 100644 install/helm/sqoop/requirements-template.yaml create mode 100644 install/helm/sqoop/templates/0-sqoop-service.yaml create mode 100644 install/helm/sqoop/templates/1-sqoop-deployment.yaml create mode 100644 install/helm/sqoop/templates/2-sqoop-clusterrole.yaml create mode 100644 install/helm/sqoop/templates/3-sqoop-clusterrole-binding.yaml create mode 100644 install/helm/sqoop/templates/4-sqoop-envoy-configmap.yaml create mode 100644 install/helm/sqoop/values-template.yaml delete mode 100644 install/kube/README.md delete mode 100644 install/kube/install.yaml delete mode 100644 mkdocs.yml create mode 100644 pin_repos.go delete mode 100644 pkg/api/types/v1/config.pb.go delete mode 100644 pkg/api/types/v1/interface.go delete mode 100644 pkg/api/types/v1/resolver_map.pb.go delete mode 100644 pkg/api/types/v1/schema.pb.go create mode 100644 pkg/api/v1/api_event_loop.sk.go create mode 100644 pkg/api/v1/api_event_loop_test.go create mode 100644 pkg/api/v1/api_snapshot.sk.go create mode 100644 pkg/api/v1/api_snapshot_emitter.sk.go create mode 100644 pkg/api/v1/api_snapshot_emitter_test.go create mode 100644 pkg/api/v1/resolver_map.pb.go create mode 100644 pkg/api/v1/resolver_map.sk.go create mode 100644 pkg/api/v1/resolver_map_client.sk.go create mode 100644 pkg/api/v1/resolver_map_client_test.go create mode 100644 pkg/api/v1/resolver_map_reconciler.sk.go create mode 100644 pkg/api/v1/schema.pb.go create mode 100644 pkg/api/v1/schema.sk.go create mode 100644 pkg/api/v1/schema_client.sk.go create mode 100644 pkg/api/v1/schema_client_test.go create mode 100644 pkg/api/v1/schema_reconciler.sk.go create mode 100644 pkg/api/v1/sqoop.solo.io_suite_test.go delete mode 100644 pkg/bootstrap/bootstrap.go delete mode 100644 pkg/bootstrap/flags/sqoop_flags.go delete mode 100644 pkg/configwatcher/config_watcher.go delete mode 100644 pkg/configwatcher/configwatcher_suite_test.go delete mode 100644 pkg/configwatcher/file_config_watcher_test.go delete mode 100644 pkg/configwatcher/interface.go delete mode 100644 pkg/core/core_suite_test.go delete mode 100644 pkg/core/core_test.go delete mode 100644 pkg/core/event_loop.go create mode 100644 pkg/defaults/main.go rename pkg/{ => engine}/dynamic/values.go (100%) rename pkg/{ => engine}/exec/executable_resolvers.go (97%) rename pkg/{ => engine}/exec/executable_schema.go (99%) create mode 100644 pkg/engine/graphql_engine.go rename pkg/{ => engine}/resolvers/factory.go (59%) rename pkg/{ => engine}/resolvers/gloo/gloo_resolvers.go (57%) rename pkg/{ => engine}/resolvers/node/node_resolvers.go (69%) rename pkg/{ => engine}/resolvers/template/template_resolvers.go (57%) create mode 100644 pkg/engine/router/landing_page.go rename pkg/{graphql => engine/router}/router.go (94%) rename pkg/{ => engine}/util/template_utils.go (95%) delete mode 100644 pkg/graphql/graphql_suite_test.go delete mode 100644 pkg/graphql/landing_page.go delete mode 100644 pkg/operator/gloo_operator.go delete mode 100644 pkg/operator/gloo_operator_test.go delete mode 100644 pkg/operator/gloo_routes.go delete mode 100644 pkg/reporter/crd_reporter_test.go delete mode 100644 pkg/reporter/interface.go delete mode 100644 pkg/reporter/reporter.go delete mode 100644 pkg/reporter/reporter_suite_test.go delete mode 100644 pkg/resolvers/gloo/gloo_suite_test.go delete mode 100644 pkg/resolvers/template/template_suite_test.go create mode 100644 pkg/setup/setup.go delete mode 100644 pkg/sqoopctl/install/docker.go delete mode 100644 pkg/sqoopctl/install/kube.go delete mode 100644 pkg/sqoopctl/install/root.go delete mode 100644 pkg/sqoopctl/resolvermap/resolvermap_create.go delete mode 100644 pkg/sqoopctl/resolvermap/resolvermap_delete.go delete mode 100644 pkg/sqoopctl/resolvermap/resolvermap_get.go delete mode 100644 pkg/sqoopctl/resolvermap/resolvermap_register.go delete mode 100644 pkg/sqoopctl/resolvermap/resolvermap_update.go delete mode 100644 pkg/sqoopctl/resolvermap/root.go delete mode 100644 pkg/sqoopctl/root.go delete mode 100644 pkg/sqoopctl/schema/root.go delete mode 100644 pkg/sqoopctl/schema/schema_create.go delete mode 100644 pkg/sqoopctl/schema/schema_delete.go delete mode 100644 pkg/sqoopctl/schema/schema_get.go delete mode 100644 pkg/sqoopctl/schema/schema_update.go delete mode 100644 pkg/storage/README.md delete mode 100644 pkg/storage/base/base_client.go delete mode 100644 pkg/storage/base/conversion.go delete mode 100644 pkg/storage/base/storable_item.go delete mode 100644 pkg/storage/consul/client_template.go.tmpl delete mode 100644 pkg/storage/consul/consul_storage_client.go delete mode 100644 pkg/storage/consul/consul_storage_client_test.go delete mode 100644 pkg/storage/consul/consul_suite_test.go delete mode 100644 pkg/storage/consul/resolver_maps.go delete mode 100644 pkg/storage/consul/schemas.go delete mode 100644 pkg/storage/crd/client/clientset/versioned/clientset.go delete mode 100644 pkg/storage/crd/client/clientset/versioned/doc.go delete mode 100644 pkg/storage/crd/client/clientset/versioned/fake/clientset_generated.go delete mode 100644 pkg/storage/crd/client/clientset/versioned/fake/doc.go delete mode 100644 pkg/storage/crd/client/clientset/versioned/fake/register.go delete mode 100644 pkg/storage/crd/client/clientset/versioned/scheme/doc.go delete mode 100644 pkg/storage/crd/client/clientset/versioned/scheme/register.go delete mode 100644 pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/doc.go delete mode 100644 pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/fake/doc.go delete mode 100644 pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/fake/fake_resolvermap.go delete mode 100644 pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/fake/fake_schema.go delete mode 100644 pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/fake/fake_solo.io_client.go delete mode 100644 pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/generated_expansion.go delete mode 100644 pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/resolvermap.go delete mode 100644 pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/schema.go delete mode 100644 pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/solo.io_client.go delete mode 100644 pkg/storage/crd/client/informers/externalversions/factory.go delete mode 100644 pkg/storage/crd/client/informers/externalversions/generic.go delete mode 100644 pkg/storage/crd/client/informers/externalversions/internalinterfaces/factory_interfaces.go delete mode 100644 pkg/storage/crd/client/informers/externalversions/solo.io/interface.go delete mode 100644 pkg/storage/crd/client/informers/externalversions/solo.io/v1/interface.go delete mode 100644 pkg/storage/crd/client/informers/externalversions/solo.io/v1/resolvermap.go delete mode 100644 pkg/storage/crd/client/informers/externalversions/solo.io/v1/schema.go delete mode 100644 pkg/storage/crd/client/listers/solo.io/v1/expansion_generated.go delete mode 100644 pkg/storage/crd/client/listers/solo.io/v1/resolvermap.go delete mode 100644 pkg/storage/crd/client/listers/solo.io/v1/schema.go delete mode 100644 pkg/storage/crd/client_template.go.tmpl delete mode 100644 pkg/storage/crd/conversion.go delete mode 100644 pkg/storage/crd/conversion_test.go delete mode 100644 pkg/storage/crd/crd_storage_client.go delete mode 100644 pkg/storage/crd/crd_storage_client_test.go delete mode 100644 pkg/storage/crd/crd_suite_test.go delete mode 100644 pkg/storage/crd/resolver_maps.go delete mode 100644 pkg/storage/crd/schemas.go delete mode 100644 pkg/storage/crd/solo.io/v1/doc.go delete mode 100644 pkg/storage/crd/solo.io/v1/register.go delete mode 100644 pkg/storage/crd/solo.io/v1/types.go delete mode 100644 pkg/storage/crd/solo.io/v1/zz_generated.deepcopy.go delete mode 100644 pkg/storage/crud/crud.go delete mode 100644 pkg/storage/errors.go delete mode 100644 pkg/storage/file/client_template.go.tmpl delete mode 100644 pkg/storage/file/directory.go delete mode 100644 pkg/storage/file/file_io.go delete mode 100644 pkg/storage/file/file_storage_client.go delete mode 100644 pkg/storage/file/file_storage_client_test.go delete mode 100644 pkg/storage/file/file_suite_test.go delete mode 100644 pkg/storage/file/resolver_maps.go delete mode 100644 pkg/storage/file/resource_ver.go delete mode 100644 pkg/storage/file/schemas.go delete mode 100644 pkg/storage/generate/generate_clients.go delete mode 100644 pkg/storage/interface.go delete mode 100644 pkg/storage/watcher.go create mode 100644 pkg/syncer/graphql_syncer.go create mode 100644 pkg/syncer/setup_syncer.go create mode 100644 pkg/todo/todos.go rename pkg/{util => translator}/generate_resolver_map.go (51%) create mode 100644 pkg/translator/routes.go create mode 100644 pkg/translator/translator.go delete mode 100644 test/kube_e2e/helpers.go delete mode 100644 test/kube_e2e/kube_resources/install.tmpl.yaml delete mode 100644 test/kube_e2e/kube_resources/testing-resources.tmpl.yaml delete mode 100644 test/kube_e2e/kubernetes_e2e_test.go delete mode 100644 test/kube_e2e/kubernetes_happy_path_test.go delete mode 100644 test/kube_e2e/kubernetes_suite_test.go rename test/{ => testdata}/helpers.go (96%) rename {examples/starwars/imported => test/testdata}/starwars/generated.go (100%) rename {examples/starwars/imported => test/testdata}/starwars/model.go (100%) rename {examples/starwars/imported => test/testdata}/starwars/models_gen.go (100%) rename {examples/starwars/imported => test/testdata}/starwars/resolvers.go (100%) rename test/{ => testdata}/utils.go (64%) rename {pkg/exec => test/unit}/executable_resolvers_test.go (70%) rename {pkg/resolvers/gloo => test/unit}/gloo_resolvers_test.go (56%) rename {pkg/dynamic => test/unit}/ordered_map_test.go (69%) rename {pkg/graphql => test/unit}/router_test.go (68%) rename pkg/dynamic/dynamic_suite_test.go => test/unit/sqoop_unit_suite_test.go (73%) rename {pkg/resolvers/template => test/unit}/template_resolvers_test.go (69%) delete mode 100644 version create mode 100644 version/check.go create mode 100644 version/linked.go diff --git a/.firebaserc b/.firebaserc deleted file mode 100644 index 864187e..0000000 --- a/.firebaserc +++ /dev/null @@ -1,14 +0,0 @@ -{ - "projects": { - "default": "solo-corp" - }, - "targets": { - "solo-corp": { - "hosting": { - "sqoop-site": [ - "sqoop-docs" - ] - } - } - } -} \ No newline at end of file diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000..9cf40b7 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,30 @@ +#!/bin/sh +# Copyright 2012 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +# git gofmt pre-commit hook +# +# To use, store as .git/hooks/pre-commit inside your repository and make sure +# it has execute permissions. +# +# This script does not handle file names that contain spaces. + +gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '\.go$') +[ -z "$gofiles" ] && exit 0 + +unformatted=$(gofmt -l $gofiles) +badimports=$(goimports -l $gofiles) +[ -z "$unformatted" ] && [ -z "$badimports" ] && exit 0 + +# Some files are not gofmt'd or goimport'd. Print message and fail. + +echo >&2 "Go files must be formatted with gofmt and goimport. Please run:" +for fn in $unformatted; do +echo >&2 " gofmt -w $PWD/$fn" +done +for fn in $badimports; do +echo >&2 " goimports -w $PWD/$fn" +done + +exit 1 diff --git a/.gitignore b/.gitignore index a1b931d..a99f073 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,10 @@ cmd/sqoopctl/sqoopctl site/ test/kube_e2e/kube_resources/test-install.yaml test/kube_e2e/kube_resources/testing-resources.yaml + +# IDE +.idea +.vscode + +# mac files +.DS_Store diff --git a/Dockerfile.site b/Dockerfile.site deleted file mode 100644 index d7ed4f9..0000000 --- a/Dockerfile.site +++ /dev/null @@ -1,2 +0,0 @@ -FROM nginx -COPY site/ /usr/share/nginx/html diff --git a/Gopkg.lock b/Gopkg.lock index 208cef5..47784ef 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -2,47 +2,210 @@ [[projects]] + digest = "1:5ad08b0e14866764a6d7475eb11c9cf05cad9a52c442593bdfa544703ff77f61" name = "cloud.google.com/go" packages = ["compute/metadata"] - revision = "0fd7230b2a7505833d5f69b75cbd6c9582401479" - version = "v0.23.0" + pruneopts = "UT" + revision = "74b12019e2aa53ec27882158f59192d7cd6d1998" + version = "v0.33.1" [[projects]] + branch = "master" + digest = "1:6da51e5ec493ad2b44cb04129e2d0a068c8fb9bd6cb5739d199573558696bb94" + name = "github.com/Azure/go-ansiterm" + packages = [ + ".", + "winterm", + ] + pruneopts = "UT" + revision = "d6e3b3328b783f23731bc4d058875b0371ff8109" + +[[projects]] + digest = "1:1fe87891e29a291377b0b2224b99fd857553dff7dce8a6ffb5fda003ce52a8b0" name = "github.com/Azure/go-autorest" packages = [ "autorest", "autorest/adal", "autorest/azure", - "autorest/date" + "autorest/date", + "logger", + "version", ] - revision = "e14a70c556c8e0db173358d1a903dca345a8e75e" + pruneopts = "UT" + revision = "4b7f49dc5db2e1e6d528524d269b4181981a7ebf" + version = "v11.1.1" + +[[projects]] + digest = "1:9f3b30d9f8e0d7040f729b82dcbc8f0dead820a133b3147ce355fc451f32d761" + name = "github.com/BurntSushi/toml" + packages = ["."] + pruneopts = "UT" + revision = "3012a1dbe2e4bd1391d42b32f0577cb7bbc7f005" + version = "v0.3.1" [[projects]] branch = "master" + digest = "1:9831b48eaba66c6eee6b8bc698d5a669088313cfee3c94435056e3522e4a53fb" + name = "github.com/MakeNowJust/heredoc" + packages = ["."] + pruneopts = "UT" + revision = "e9091a26100e9cfb2b6a8f470085bfa541931a91" + +[[projects]] + digest = "1:3b10c6fd33854dc41de2cf78b7bae105da94c2789b6fa5b9ac9e593ea43484ac" + name = "github.com/Masterminds/goutils" + packages = ["."] + pruneopts = "UT" + revision = "41ac8693c5c10a92ea1ff5ac3a7f95646f6123b0" + version = "v1.1.0" + +[[projects]] + digest = "1:55388fd080150b9a072912f97b1f5891eb0b50df43401f8b75fb4273d3fec9fc" + name = "github.com/Masterminds/semver" + packages = ["."] + pruneopts = "UT" + revision = "c7af12943936e8c39859482e61f0574c2fd7fc75" + version = "v1.4.2" + +[[projects]] + digest = "1:d8cbb69f08bd6cdf2e5d36b4784139920121943e8a3e5661431052f8e785f58b" + name = "github.com/Masterminds/sprig" + packages = ["."] + pruneopts = "UT" + revision = "b1fe2752acccf8c3d7f8a1e7c75c7ae7d83a1975" + version = "v2.18.0" + +[[projects]] + digest = "1:a2682518d905d662d984ef9959984ef87cecb777d379bfa9d9fe40e78069b3e4" + name = "github.com/PuerkitoBio/purell" + packages = ["."] + pruneopts = "UT" + revision = "44968752391892e1b0d0b821ee79e9a85fa13049" + version = "v1.1.1" + +[[projects]] + branch = "master" + digest = "1:c739832d67eb1e9cc478a19cc1a1ccd78df0397bf8a32978b759152e205f644b" + name = "github.com/PuerkitoBio/urlesc" + packages = ["."] + pruneopts = "UT" + revision = "de5bf2ad457846296e2031421a34e2568e304e35" + +[[projects]] + digest = "1:87c2e02fb01c27060ccc5ba7c5a407cc91147726f8f40b70cceeedbc52b1f3a8" + name = "github.com/Sirupsen/logrus" + packages = ["."] + pruneopts = "UT" + revision = "e1e72e9de974bd926e5c56f83753fba2df402ce5" + version = "v1.3.0" + +[[projects]] + branch = "master" + digest = "1:ef5b0622d834c139454148b8fd0c92bb314828900532b267ae62da9fec109866" name = "github.com/armon/go-metrics" packages = ["."] - revision = "783273d703149aaeb9897cf58613d5af48861c25" + pruneopts = "UT" + revision = "f0300d1749da6fa982027e449ec0c7a145510c3c" + +[[projects]] + branch = "master" + digest = "1:d6afaeed1502aa28e80a4ed0981d570ad91b2579193404256ce672ed0a609e0d" + name = "github.com/beorn7/perks" + packages = ["quantile"] + pruneopts = "UT" + revision = "3a771d992973f24aa725d07868b467d1ddfceafb" + +[[projects]] + branch = "master" + digest = "1:7310ec5042e3a0dd73ed9f23f812e73ce0ae88d08039e4473472ebba20bf2996" + name = "github.com/bxcodec/faker" + packages = [ + ".", + "support/slice", + ] + pruneopts = "UT" + revision = "bf89c38ff1742f295e15df0bd5e8b3e81da95855" + +[[projects]] + branch = "master" + digest = "1:95e08278c876d185ba67533f045e9e63b3c9d02cbd60beb0f4dbaa2344a13ac2" + name = "github.com/chai2010/gettext-go" + packages = [ + "gettext", + "gettext/mo", + "gettext/plural", + "gettext/po", + ] + pruneopts = "UT" + revision = "bf70f2a70fb1b1f36d90d671a72795984eab0fcb" + +[[projects]] + digest = "1:7cb4fdca4c251b3ef8027c90ea35f70c7b661a593b9eeae34753c65499098bb1" + name = "github.com/cpuguy83/go-md2man" + packages = ["md2man"] + pruneopts = "UT" + revision = "20f5889cbdc3c73dbd2862796665e7c465ade7d1" + version = "v1.0.8" [[projects]] - name = "github.com/d4l3k/messagediff" + digest = "1:ec66ad050342a3573ed2f5a4337d51b4c6d5d2a717cc6c9ecf86b081235a5759" + name = "github.com/cyphar/filepath-securejoin" packages = ["."] - revision = "29f32d820d112dbd66e58492a6ffb7cc3106312b" - version = "v1.2.1" + pruneopts = "UT" + revision = "a261ee33d7a517f054effbf451841abaafe3e0fd" + version = "v0.2.2" [[projects]] + digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39" name = "github.com/davecgh/go-spew" packages = ["spew"] + pruneopts = "UT" revision = "346938d642f2ec3594ed81d874461961cd0faa76" version = "v1.1.0" [[projects]] + digest = "1:76dc72490af7174349349838f2fe118996381b31ea83243812a97e5a0fd5ed55" name = "github.com/dgrijalva/jwt-go" packages = ["."] + pruneopts = "UT" revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e" version = "v3.2.0" +[[projects]] + digest = "1:4ddc17aeaa82cb18c5f0a25d7c253a10682f518f4b2558a82869506eec223d76" + name = "github.com/docker/distribution" + packages = [ + "digestset", + "reference", + ] + pruneopts = "UT" + revision = "2461543d988979529609e8cb6fca9ca190dc48da" + version = "v2.7.1" + +[[projects]] + digest = "1:53e99d883df3e940f5f0223795f300eb32b8c044f226132bfc0e74930f24ea4b" + name = "github.com/docker/docker" + packages = [ + "pkg/term", + "pkg/term/windows", + ] + pruneopts = "UT" + revision = "092cba3727bb9b4a2f0e922cd6c0f93ea270e363" + version = "v1.13.1" + [[projects]] branch = "master" + digest = "1:ecdc8e0fe3bc7d549af1c9c36acf3820523b707d6c071b6d0c3860882c6f7b42" + name = "github.com/docker/spdystream" + packages = [ + ".", + "spdy", + ] + pruneopts = "UT" + revision = "6480d4af844c189cf5dd913db24ddd339d3a4f85" + +[[projects]] + digest = "1:c76178e75235ac60b036c8aa39e870ee248c3ef82e6ce70a01806d870cec7383" name = "github.com/envoyproxy/go-control-plane" packages = [ "envoy/api/v2", @@ -52,54 +215,138 @@ "envoy/api/v2/endpoint", "envoy/api/v2/listener", "envoy/api/v2/route", - "envoy/config/filter/accesslog/v2", - "envoy/config/filter/network/http_connection_manager/v2", + "envoy/service/discovery/v2", "envoy/type", - "pkg/util" ] - revision = "39c73ae5ba014b648d4164a24340a78b6b761978" + pruneopts = "UT" + revision = "6bb1dbbfcb2b3192849c3f0e6d3a6a6f038ded8b" + +[[projects]] + digest = "1:f1f2bd73c025d24c3b93abf6364bccb802cf2fdedaa44360804c67800e8fab8d" + name = "github.com/evanphx/json-patch" + packages = ["."] + pruneopts = "UT" + revision = "72bf35d0ff611848c1dc9df0f976c81192392fa5" + version = "v4.1.0" [[projects]] + branch = "master" + digest = "1:5e0da1aba1a7b125f46e6ddca43e98b40cf6eaea3322b016c331cf6afe53c30a" + name = "github.com/exponent-io/jsonpath" + packages = ["."] + pruneopts = "UT" + revision = "d6023ce2651d8eafb5c75bb0c7167536102ec9f5" + +[[projects]] + digest = "1:af43bdaaf86655a2343f113e9b293bbc16b12099eaeb223982bbe4d4c22ba14d" name = "github.com/fatih/structs" packages = ["."] - revision = "a720dfa8df582c51dee1b36feabb906bde1588bd" - version = "v1.0" + pruneopts = "UT" + revision = "4966fc68f5b7593aafa6cbbba2d65ec6e1416047" + version = "v1.1.0" [[projects]] + digest = "1:58981659bb698cb749d5bf7316e71e2768ffed5fd35f6ba3b182dfa1fed13420" + name = "github.com/fgrosse/zaptest" + packages = ["."] + pruneopts = "UT" + revision = "88dc3e74eadbccae7fe0cce56e0627cb7bc35b97" + version = "v1.0.0" + +[[projects]] + digest = "1:2cd7915ab26ede7d95b8749e6b1f933f1c6d5398030684e6505940a10f31cfda" name = "github.com/ghodss/yaml" packages = ["."] + pruneopts = "UT" revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7" version = "v1.0.0" [[projects]] + digest = "1:953a2628e4c5c72856b53f5470ed5e071c55eccf943d798d42908102af2a610f" + name = "github.com/go-openapi/jsonpointer" + packages = ["."] + pruneopts = "UT" + revision = "ef5f0afec364d3b9396b7b77b43dbe26bf1f8004" + version = "v0.18.0" + +[[projects]] + digest = "1:81210e0af657a0fb3638932ec68e645236bceefa4c839823db0c4d918f080895" + name = "github.com/go-openapi/jsonreference" + packages = ["."] + pruneopts = "UT" + revision = "8483a886a90412cd6858df4ea3483dce9c8e35a3" + version = "v0.18.0" + +[[projects]] + digest = "1:08656ef9c5a45ddccb7f206ca2d67e12e9fcda4122a83dc0544b5c967267cefa" + name = "github.com/go-openapi/spec" + packages = ["."] + pruneopts = "UT" + revision = "5b6cdde3200976e3ecceb2868706ee39b6aff3e4" + version = "v0.18.0" + +[[projects]] + digest = "1:0005186c6608dd542239ac8e4f4f1e2e7c24d493e999113c46b93332f0362fc0" + name = "github.com/go-openapi/swag" + packages = ["."] + pruneopts = "UT" + revision = "1d29f06aebd59ccdf11ae04aa0334ded96e2d909" + version = "v0.18.0" + +[[projects]] + digest = "1:9ae31ce33b4bab257668963e844d98765b44160be4ee98cafc44637a213e530d" + name = "github.com/gobwas/glob" + packages = [ + ".", + "compiler", + "match", + "syntax", + "syntax/ast", + "syntax/lexer", + "util/runes", + "util/strings", + ] + pruneopts = "UT" + revision = "5ccd90ef52e1e632236f7326478d4faa74f99438" + version = "v0.2.3" + +[[projects]] + digest = "1:d661a50b937259fd2a6324af7ee4cb62fa506bd9b82cd2fd96e23122af7c006f" name = "github.com/gogo/googleapis" packages = [ "google/api", - "google/rpc" + "google/rpc", ] + pruneopts = "UT" revision = "08a7655d27152912db7aaf4f983275eaf8d128ef" version = "v1.0.0" [[projects]] + digest = "1:dc9cbd16a546ee8d4a9f4823c9c91f0825e294fc085493a5a3937183d5f17b84" name = "github.com/gogo/protobuf" packages = [ "gogoproto", "jsonpb", "proto", "protoc-gen-gogo/descriptor", + "protoc-gen-gogo/plugin", "sortkeys", - "types" + "types", ] - revision = "342cbe0a04158f6dcb03ca0079991a51a4248c02" - version = "v0.5" + pruneopts = "UT" + revision = "4cbf7e384e768b4e01799441fdf2a706a5635ae7" + version = "v1.2.0" [[projects]] branch = "master" - name = "github.com/golang/glog" - packages = ["."] - revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998" + digest = "1:b7cb6054d3dff43b38ad2e92492f220f57ae6087ee797dca298139776749ace8" + name = "github.com/golang/groupcache" + packages = ["lru"] + pruneopts = "UT" + revision = "5b532d6fd5efaf7fa130d4e859a2fde0fc3a9e1b" [[projects]] + digest = "1:fc5af0ce30d523e01d57e3cf514ea481373cccb512d5224511864505bd397187" name = "github.com/golang/protobuf" packages = [ "proto", @@ -108,35 +355,59 @@ "ptypes", "ptypes/any", "ptypes/duration", - "ptypes/timestamp" + "ptypes/timestamp", ] - revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265" - version = "v1.1.0" + pruneopts = "UT" + revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5" + version = "v1.2.0" [[projects]] branch = "master" + digest = "1:4a0c6bb4805508a6287675fac876be2ac1182539ca8a32468d8128882e9d5009" + name = "github.com/golang/snappy" + packages = ["."] + pruneopts = "UT" + revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a" + +[[projects]] + branch = "master" + digest = "1:9887333bbef17574b1db5f9893ea137ac44107235d624408a3ac9e0b98fbb2cb" name = "github.com/google/btree" packages = ["."] + pruneopts = "UT" revision = "e89373fe6b4a7413d7acd6da1725b83ef713e6e4" [[projects]] branch = "master" + digest = "1:3ee90c0d94da31b442dde97c99635aaafec68d0b8a3c12ee2075c6bdabeec6bb" name = "github.com/google/gofuzz" packages = ["."] + pruneopts = "UT" revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1" [[projects]] + digest = "1:236d7e1bdb50d8f68559af37dbcf9d142d56b431c9b2176d41e2a009b664cda8" + name = "github.com/google/uuid" + packages = ["."] + pruneopts = "UT" + revision = "9b3b1e0f5f99ae461456d768e7d301a7acdaa2d8" + version = "v1.1.0" + +[[projects]] + digest = "1:65c4414eeb350c47b8de71110150d0ea8a281835b1f386eacaa3ad7325929c21" name = "github.com/googleapis/gnostic" packages = [ "OpenAPIv2", "compiler", - "extensions" + "extensions", ] + pruneopts = "UT" revision = "7c663266750e7d82587642f65e60bc4083f1f84e" version = "v0.2.0" [[projects]] branch = "master" + digest = "1:0f81ad0b38cbd916ca43a8701dc2507c3f3fce9955fc787318fcc440fef9db56" name = "github.com/gophercloud/gophercloud" packages = [ ".", @@ -145,108 +416,139 @@ "openstack/identity/v2/tokens", "openstack/identity/v3/tokens", "openstack/utils", - "pagination" + "pagination", ] - revision = "7112fcd50da4ea27e8d4d499b30f04eea143bec2" - -[[projects]] - name = "github.com/gorhill/cronexpr" - packages = ["."] - revision = "a557574d6c024ed6e36acc8b610f5f211c91568a" - version = "1.0.0" + pruneopts = "UT" + revision = "a9f90060ebd9d4e0be53e429d2623746b700468c" [[projects]] + digest = "1:c79fb010be38a59d657c48c6ba1d003a8aa651fa56b579d959d74573b7dff8e1" name = "github.com/gorilla/context" packages = ["."] + pruneopts = "UT" revision = "08b5f424b9271eedf6f9f0ce86cb9396ed337a42" version = "v1.1.1" [[projects]] + digest = "1:e73f5b0152105f18bc131fba127d9949305c8693f8a762588a82a48f61756f5f" name = "github.com/gorilla/mux" packages = ["."] + pruneopts = "UT" revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf" version = "v1.6.2" [[projects]] + digest = "1:cee8e8ac80df6373e7daa11baf1f98c1b6f7242c49ccae7e1ec34a971dc408d9" name = "github.com/gorilla/websocket" packages = ["."] - revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b" - version = "v1.2.0" + pruneopts = "UT" + revision = "3ff3320c2a1756a3691521efc290b4701575147c" + version = "v1.3.0" [[projects]] branch = "master" + digest = "1:86c1210529e69d69860f2bb3ee9ccce0b595aa3f9165e7dd1388e5c612915888" name = "github.com/gregjones/httpcache" packages = [ ".", - "diskcache" + "diskcache", ] + pruneopts = "UT" revision = "9cad4c3443a7200dd6400aef47183728de563a38" [[projects]] + digest = "1:9b7a07ac7577787a8ecc1334cb9f34df1c76ed82a917d556c5713d3ab84fbc43" + name = "github.com/grpc-ecosystem/go-grpc-prometheus" + packages = ["."] + pruneopts = "UT" + revision = "c225b8c3b01faf2899099b768856a9e916e5087b" + version = "v1.2.0" + +[[projects]] + digest = "1:bfc483a051d3c7185ebeaa41b5bb67a4f76e742217bcaeab5661cc4b1320f392" name = "github.com/hashicorp/consul" packages = ["api"] - revision = "39f93f011e591c842acc8053a7f5972aa6e592fd" - version = "v1.2.1" + pruneopts = "UT" + revision = "eba25a143821cec25bdb48fe64dd7b7a6152d01d" [[projects]] branch = "master" + digest = "1:d1971637b21871ec2033a44ca87c99c5608a7340cb34ec75fab8d2ab503276c9" name = "github.com/hashicorp/errwrap" packages = ["."] - revision = "7554cd9344cec97297fa6649b055a8c98c2a1e55" + pruneopts = "UT" + revision = "d6c0cd88035724dd42e0f335ae30161c20575ecc" [[projects]] branch = "master" + digest = "1:77cb3be9b21ba7f1a4701e870c84ea8b66e7d74c7c8951c58155fdadae9414ec" name = "github.com/hashicorp/go-cleanhttp" packages = ["."] + pruneopts = "UT" revision = "d5fe4b57a186c716b0e00b8c301cbd9b4182694d" [[projects]] - branch = "master" + digest = "1:2be5a35f0c5b35162c41bb24971e5dcf6ce825403296ee435429cdcc4e1e847e" name = "github.com/hashicorp/go-immutable-radix" packages = ["."] - revision = "7f3cd4390caab3250a57f30efdb2a65dd7649ecf" + pruneopts = "UT" + revision = "27df80928bb34bb1b0d6d0e01b9e679902e7a6b5" + version = "v1.0.0" [[projects]] branch = "master" - name = "github.com/hashicorp/go-msgpack" - packages = ["codec"] - revision = "fa3f63826f7c23912c15263591e65d54d080b458" + digest = "1:46fb6a9f1b9667f32ac93e08b1da118b2c666991424ea12e848b05d4fe5155ef" + name = "github.com/hashicorp/go-multierror" + packages = ["."] + pruneopts = "UT" + revision = "3d5d8f294aa03d8e98859feac328afbdf1ae0703" [[projects]] branch = "master" - name = "github.com/hashicorp/go-multierror" + digest = "1:183f00c472fb9b2446659618eebf4899872fa267b92f926539411abdc8b941df" + name = "github.com/hashicorp/go-retryablehttp" packages = ["."] - revision = "b7773ae218740a7be65057fc60b366a49b538a44" + pruneopts = "UT" + revision = "e651d75abec6fbd4f2c09508f72ae7af8a8b7171" [[projects]] branch = "master" + digest = "1:45aad874d3c7d5e8610427c81870fb54970b981692930ec2a319ce4cb89d7a00" name = "github.com/hashicorp/go-rootcerts" packages = ["."] + pruneopts = "UT" revision = "6bb64b370b90e7ef1fa532be9e591a81c3493e00" [[projects]] branch = "master" - name = "github.com/hashicorp/go-uuid" + digest = "1:14f2005c31ddf99c4a0f36fc440f8d1ac43224194c7c4a904b3c8f4ba5654d0b" + name = "github.com/hashicorp/go-sockaddr" packages = ["."] - revision = "27454136f0364f2d44b1276c552d69105cf8c498" + pruneopts = "UT" + revision = "6d291a969b86c4b633730bfc6b8b9d64c3aafed9" [[projects]] branch = "master" - name = "github.com/hashicorp/go-version" + digest = "1:f14364057165381ea296e49f8870a9ffce2b8a95e34d6ae06c759106aaef428c" + name = "github.com/hashicorp/go-uuid" packages = ["."] - revision = "23480c0665776210b5fbbac6eaaee40e3e6a96b7" + pruneopts = "UT" + revision = "4f571afc59f3043a65f8fe6bf46d887b10a01d43" [[projects]] branch = "master" + digest = "1:cf296baa185baae04a9a7004efee8511d08e2f5f51d4cbe5375da89722d681db" name = "github.com/hashicorp/golang-lru" packages = [ ".", - "simplelru" + "simplelru", ] + pruneopts = "UT" revision = "0fb14efe8c47ae851c0034ed7a448854d3d34cf3" [[projects]] branch = "master" + digest = "1:12247a2e99a060cc692f6680e5272c8adf0b8f572e6bce0d7095e624c958a240" name = "github.com/hashicorp/hcl" packages = [ ".", @@ -257,148 +559,244 @@ "hcl/token", "json/parser", "json/scanner", - "json/token" + "json/token", ] + pruneopts = "UT" revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168" [[projects]] - name = "github.com/hashicorp/nomad" + digest = "1:acc81e4e4289587b257ccdfccbc6eaf16d4c2fb57dda73c6bb349bf50f02501f" + name = "github.com/hashicorp/serf" + packages = ["coordinate"] + pruneopts = "UT" + revision = "19bbd39e421bdf3559d5025fb2c760f5ffa56233" + +[[projects]] + digest = "1:31e20c290aaed8b03e36350241a9f862e4fd53d11780a41d231771ace9a1be03" + name = "github.com/hashicorp/vault" packages = [ - "acl", "api", - "api/contexts", - "helper", - "helper/args", - "helper/flatmap", - "helper/uuid", - "nomad/structs" + "helper/compressutil", + "helper/hclutil", + "helper/jsonutil", + "helper/parseutil", + "helper/strutil", ] - revision = "1c75593351b712bb2c22339efd642f1518ae837f" + pruneopts = "UT" + revision = "e21712a687889de1125e0a12a980420b1a4f72d3" + version = "v0.10.4" [[projects]] - name = "github.com/hashicorp/raft" - packages = ["."] - revision = "6d14f0c70869faabd9e60ba7ed88a6cbbd6a661f" - version = "v1.0.0" + branch = "master" + digest = "1:59392ed8afb901aab4287d4894df8191722e34f3957716f4350c8c133ce99046" + name = "github.com/hpcloud/tail" + packages = [ + ".", + "ratelimiter", + "util", + "watch", + "winfile", + ] + pruneopts = "UT" + revision = "a1dbeea552b7c8df4b542c66073e393de198a800" [[projects]] - name = "github.com/hashicorp/serf" - packages = ["coordinate"] - revision = "d6574a5bb1226678d7010325fb6c985db20ee458" - version = "v0.8.1" + digest = "1:f9a5e090336881be43cfc1cf468330c1bdd60abdc9dd194e0b1ab69f4b94dd7c" + name = "github.com/huandu/xstrings" + packages = ["."] + pruneopts = "UT" + revision = "f02667b379e2fb5916c3cda2cf31e0eb885d79f8" + version = "v1.2.0" [[projects]] branch = "master" - name = "github.com/howeyc/gopass" + digest = "1:89cd2208bdeb192cc83ee287216af3c987fc87a90b9087718ad6631b636cefcf" + name = "github.com/iancoleman/strcase" packages = ["."] - revision = "bf9dde6d0d2c004a008c27aaee91170c786f6db8" + pruneopts = "UT" + revision = "3605ed457bf7f8caa1371b4fafadadc026673479" [[projects]] + digest = "1:5258ca648f4cf40b7bc7c52b85009546699332616f9ed23d89b6798baca6f707" name = "github.com/ilackarms/protoc-gen-doc" packages = ["."] + pruneopts = "UT" revision = "728a624b38e5ae90bb3e5da5f406e05138f1517c" version = "v1.0.0" [[projects]] + digest = "1:99bafffd60368afd17d11302671600e044137f5c6ba9babb950a47c84ed5a71d" + name = "github.com/ilackarms/protokit" + packages = ["."] + pruneopts = "UT" + revision = "ee2393f3bbf04700fc5d611077496c47ec1937ed" + +[[projects]] + digest = "1:3e260afa138eab6492b531a3b3d10ab4cb70512d423faa78b8949dec76e66a21" name = "github.com/imdario/mergo" packages = ["."] - revision = "9d5f1277e9a8ed20c3684bda8fde67c05628518c" - version = "v0.3.4" + pruneopts = "UT" + revision = "9316a62528ac99aaecb4e47eadd6dc8aa6533d58" + version = "v0.3.5" [[projects]] + digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" name = "github.com/inconshreveable/mousetrap" packages = ["."] + pruneopts = "UT" revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" version = "v1.0" [[projects]] + digest = "1:eaefc85d32c03e5f0c2b88ea2f79fce3d993e2c78316d21319575dd4ea9153ca" name = "github.com/json-iterator/go" packages = ["."] - revision = "ca39e5af3ece67bbcda3d0f4f56a8e24d9f2dad4" - version = "1.1.3" - -[[projects]] - name = "github.com/juju/ratelimit" - packages = ["."] - revision = "59fac5042749a5afb9af70e813da1dd5474f0167" - version = "1.0.1" + pruneopts = "UT" + revision = "ab8a2e0c74be9d3be70b3184d9acc634935ded82" + version = "1.1.4" [[projects]] + digest = "1:16dd6b893b78a50564cdde1d9f7ea67224dece11bb0886bd882f1dc3dc1d440d" name = "github.com/k0kubun/pp" packages = ["."] + pruneopts = "UT" revision = "027a6d1765d673d337e687394dbe780dd64e2a1e" version = "v2.3.0" [[projects]] branch = "master" - name = "github.com/keybase/go-ps" - packages = [ - ".", - "darwincgo" - ] - revision = "668c8856d9992f97248b3177d45743d2cc1068db" + digest = "1:400e113a367b511b9b09ca642ee11d9885485a93838526d697033af334a2fdde" + name = "github.com/kballard/go-shellquote" + packages = ["."] + pruneopts = "UT" + revision = "95032a82bc518f77982ea72343cc1ade730072f0" [[projects]] + digest = "1:0a69a1c0db3591fcefb47f115b224592c8dfa4368b7ba9fae509d5e16cdc95c8" + name = "github.com/konsorten/go-windows-terminal-sequences" + packages = ["."] + pruneopts = "UT" + revision = "5c8c8bd35d3832f5d134ae1e1e375b69a4d25242" + version = "v1.0.1" + +[[projects]] + digest = "1:dfe5159383dfcd370555c35374ac99022906f987b55cc65a830fb9ced970b4b1" name = "github.com/lyft/protoc-gen-validate" packages = ["validate"] + pruneopts = "UT" revision = "930a67cf7ba41b9d9436ad7a1be70a5d5ff6e1fc" version = "v0.0.6" [[projects]] + branch = "master" + digest = "1:84a5a2b67486d5d67060ac393aa255d05d24ed5ee41daecd5635ec22657b6492" + name = "github.com/mailru/easyjson" + packages = [ + "buffer", + "jlexer", + "jwriter", + ] + pruneopts = "UT" + revision = "60711f1a8329503b04e1c88535f419d0bb440bff" + +[[projects]] + digest = "1:c658e84ad3916da105a761660dcaeb01e63416c8ec7bc62256a9b411a05fcd67" name = "github.com/mattn/go-colorable" packages = ["."] + pruneopts = "UT" revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072" version = "v0.0.9" [[projects]] + digest = "1:d4d17353dbd05cb52a2a52b7fe1771883b682806f68db442b436294926bbfafb" name = "github.com/mattn/go-isatty" packages = ["."] + pruneopts = "UT" revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39" version = "v0.0.3" +[[projects]] + digest = "1:0356f3312c9bd1cbeda81505b7fd437501d8e778ab66998ef69f00d7f9b3a0d7" + name = "github.com/mattn/go-runewidth" + packages = ["."] + pruneopts = "UT" + revision = "3ee7d812e62a0804a7d0a324e0249ca2db3476d3" + version = "v0.0.4" + +[[projects]] + digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc" + name = "github.com/matttproud/golang_protobuf_extensions" + packages = ["pbutil"] + pruneopts = "UT" + revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" + version = "v1.0.1" + [[projects]] branch = "master" - name = "github.com/mitchellh/copystructure" + digest = "1:2b32af4d2a529083275afc192d1067d8126b578c7a9613b26600e4df9c735155" + name = "github.com/mgutz/ansi" packages = ["."] - revision = "d23ffcb85de31694d6ccaa23ccb4a03e55c1303f" + pruneopts = "UT" + revision = "9520e82c474b0a04dd04f8a40959027271bab992" [[projects]] branch = "master" + digest = "1:8eb17c2ec4df79193ae65b621cd1c0c4697db3bc317fe6afdc76d7f2746abd05" name = "github.com/mitchellh/go-homedir" packages = ["."] + pruneopts = "UT" revision = "3864e76763d94a6df2f9960b16a20a33da9f9a66" +[[projects]] + digest = "1:abf08734a6527df70ed361d7c369fb580e6840d8f7a6012e5f609fdfd93b4e48" + name = "github.com/mitchellh/go-wordwrap" + packages = ["."] + pruneopts = "UT" + revision = "9e67c67572bc5dd02aef930e2b0ae3c02a4b5a5c" + version = "v1.0.0" + [[projects]] branch = "master" + digest = "1:544635141f5fb084637823f438f3563be046a9730c256d2e7760dbb741cd88b5" name = "github.com/mitchellh/hashstructure" packages = ["."] + pruneopts = "UT" revision = "2bca23e0e452137f789efbc8610126fd8b94f73b" [[projects]] branch = "master" + digest = "1:5ab79470a1d0fb19b041a624415612f8236b3c06070161a910562f2b2d064355" name = "github.com/mitchellh/mapstructure" packages = ["."] + pruneopts = "UT" revision = "f15292f7a699fcc1a38a80977f80a046874ba8ac" [[projects]] - branch = "master" - name = "github.com/mitchellh/reflectwalk" - packages = ["."] - revision = "63d60e9d0dbc60cf9164e6510889b0db6683d98c" - -[[projects]] + digest = "1:33422d238f147d247752996a26574ac48dcf472976eda7f5134015f06bf16563" name = "github.com/modern-go/concurrent" packages = ["."] + pruneopts = "UT" revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94" version = "1.0.3" [[projects]] + digest = "1:e32bdbdb7c377a07a9a46378290059822efdce5c8d96fe71940d87cb4f918855" name = "github.com/modern-go/reflect2" packages = ["."] - revision = "1df9eeb2bb81f327b96228865c5687bc2194af3f" - version = "1.0.0" + pruneopts = "UT" + revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd" + version = "1.0.1" + +[[projects]] + digest = "1:abcdbf03ca6ca13d3697e2186edc1f33863bbdac2b3a44dfa39015e8903f7409" + name = "github.com/olekukonko/tablewriter" + packages = ["."] + pruneopts = "UT" + revision = "e6d60cf7ba1f42d86d54cdf5508611c4aafb3970" + version = "v0.0.1" [[projects]] + digest = "1:42e29deef12327a69123b9cb2cb45fee4af5c12c2a23c6e477338279a052703f" name = "github.com/onsi/ginkgo" packages = [ ".", @@ -418,12 +816,14 @@ "reporters/stenographer", "reporters/stenographer/support/go-colorable", "reporters/stenographer/support/go-isatty", - "types" + "types", ] - revision = "fa5fabab2a1bfbd924faf4c067d07ae414e2aedf" - version = "v1.5.0" + pruneopts = "UT" + revision = "3774a09d95489ccaa16032e0770d08ea77ba6184" + version = "v1.6.0" [[projects]] + digest = "1:29f294e6a3b9d30629266b2765a8c203056387941e600405b8e8871c84653042" name = "github.com/onsi/gomega" packages = [ ".", @@ -437,112 +837,271 @@ "matchers/support/goraph/edge", "matchers/support/goraph/node", "matchers/support/goraph/util", - "types" + "types", ] - revision = "62bff4df71bdbc266561a0caee19f0594b17c240" - version = "v1.4.0" + pruneopts = "UT" + revision = "b6ea1ea48f981d0f615a154a45eabb9dd466556d" + version = "v1.4.1" + +[[projects]] + digest = "1:ee4d4af67d93cc7644157882329023ce9a7bcfce956a079069a9405521c7cc8d" + name = "github.com/opencontainers/go-digest" + packages = ["."] + pruneopts = "UT" + revision = "279bed98673dd5bef374d3b6e4b09e2af76183bf" + version = "v1.0.0-rc1" + +[[projects]] + digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e" + name = "github.com/pelletier/go-toml" + packages = ["."] + pruneopts = "UT" + revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194" + version = "v1.2.0" [[projects]] branch = "master" + digest = "1:3bf17a6e6eaa6ad24152148a631d18662f7212e21637c2699bff3369b7f00fa2" name = "github.com/petar/GoLLRB" packages = ["llrb"] + pruneopts = "UT" revision = "53be0d36a84c2a886ca057d34b6aa4468df9ccb4" [[projects]] + digest = "1:0e7775ebbcf00d8dd28ac663614af924411c868dca3d5aa762af0fae3808d852" name = "github.com/peterbourgon/diskv" packages = ["."] + pruneopts = "UT" revision = "5f041e8faa004a95c88a202771f4cc3e991971e6" version = "v2.0.1" [[projects]] + digest = "1:cf31692c14422fa27c83a05292eb5cbe0fb2775972e8f1f8446a71549bd8980b" name = "github.com/pkg/errors" packages = ["."] - revision = "645ef00459ed84a119197bfb8d8205042c6df63d" - version = "v0.8.0" + pruneopts = "UT" + revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4" + version = "v0.8.1" + +[[projects]] + digest = "1:93a746f1060a8acbcf69344862b2ceced80f854170e1caae089b2834c5fbf7f4" + name = "github.com/prometheus/client_golang" + packages = [ + "prometheus", + "prometheus/internal", + "prometheus/promhttp", + ] + pruneopts = "UT" + revision = "505eaef017263e299324067d40ca2c48f6a2cf50" + version = "v0.9.2" + +[[projects]] + branch = "master" + digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4" + name = "github.com/prometheus/client_model" + packages = ["go"] + pruneopts = "UT" + revision = "fd36f4220a901265f90734c3183c5f0c91daa0b8" + +[[projects]] + digest = "1:35cf6bdf68db765988baa9c4f10cc5d7dda1126a54bd62e252dbcd0b1fc8da90" + name = "github.com/prometheus/common" + packages = [ + "expfmt", + "internal/bitbucket.org/ww/goautoneg", + "model", + ] + pruneopts = "UT" + revision = "cfeb6f9992ffa54aaa4f2170ade4067ee478b250" + version = "v0.2.0" + +[[projects]] + branch = "master" + digest = "1:f0bb332b39488b057a8671557f307997426e5c650ee738a634a0697a41e207c5" + name = "github.com/prometheus/procfs" + packages = [ + ".", + "internal/util", + "nfs", + "xfs", + ] + pruneopts = "UT" + revision = "f8d8b3f739bd91a7c0462cb55235ef63c79c9abc" [[projects]] + digest = "1:d7aa51c836ace8d00a3d88b4058c470624bdd7045cd3b0f58c2d6ba7ab4433dc" name = "github.com/pseudomuto/protoc-gen-doc" packages = ["parser"] + pruneopts = "UT" revision = "728a624b38e5ae90bb3e5da5f406e05138f1517c" version = "v1.0.0" [[projects]] + digest = "1:0665976cc6d8c2e60dafca32820b6b7eca70f37ded0bcccc103388e1c0d05c53" name = "github.com/radovskyb/watcher" packages = ["."] + pruneopts = "UT" revision = "6145e1439b9de93806925353403f91d2abbad8a5" version = "v1.0.2" [[projects]] + digest = "1:b36a0ede02c4c2aef7df7f91cbbb7bb88a98b5d253509d4f997dda526e50c88c" + name = "github.com/russross/blackfriday" + packages = ["."] + pruneopts = "UT" + revision = "05f3235734ad95d0016f6a23902f06461fcf567a" + version = "v1.5.2" + +[[projects]] + digest = "1:0e792eea6c96ec55ff302ef33886acbaa5006e900fefe82689e88d96439dcd84" + name = "github.com/ryanuber/go-glob" + packages = ["."] + pruneopts = "UT" + revision = "572520ed46dbddaed19ea3d9541bdd0494163693" + version = "v0.1" + +[[projects]] + digest = "1:01412129fc2d8d2280aa4f48c62902c820aa4df772f0f50eda7e3a40d9a8421e" name = "github.com/solo-io/gloo" packages = [ - "pkg/api/types/v1", - "pkg/backoff", - "pkg/bootstrap", - "pkg/bootstrap/configstorage", - "pkg/bootstrap/flags", - "pkg/control-plane/filewatcher", - "pkg/coreplugins/common", - "pkg/coreplugins/static", - "pkg/endpointdiscovery", - "pkg/log", - "pkg/plugins", - "pkg/plugins/common/transformation", - "pkg/plugins/rest", - "pkg/protoutil", - "pkg/secretwatcher", - "pkg/signals", - "pkg/storage", - "pkg/storage/base", - "pkg/storage/consul", - "pkg/storage/crd", - "pkg/storage/crd/client/clientset/versioned", - "pkg/storage/crd/client/clientset/versioned/scheme", - "pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1", - "pkg/storage/crd/solo.io/v1", - "pkg/storage/crud", - "pkg/storage/dependencies", - "pkg/storage/file", - "pkg/utils/kube", - "test/helpers", - "test/helpers/local" + "install/helm/gloo/generate", + "pkg/cliutil", + "pkg/cliutil/install", + "pkg/utils/setuputils", + "pkg/version", + "projects/gateway/pkg/api/v1", + "projects/gloo/cli/pkg/constants", + "projects/gloo/cli/pkg/helpers", + "projects/gloo/cli/pkg/printers", + "projects/gloo/pkg/api/converters/kube", + "projects/gloo/pkg/api/v1", + "projects/gloo/pkg/api/v1/plugins", + "projects/gloo/pkg/api/v1/plugins/aws", + "projects/gloo/pkg/api/v1/plugins/azure", + "projects/gloo/pkg/api/v1/plugins/consul", + "projects/gloo/pkg/api/v1/plugins/faultinjection", + "projects/gloo/pkg/api/v1/plugins/grpc", + "projects/gloo/pkg/api/v1/plugins/kubernetes", + "projects/gloo/pkg/api/v1/plugins/rest", + "projects/gloo/pkg/api/v1/plugins/retries", + "projects/gloo/pkg/api/v1/plugins/static", + "projects/gloo/pkg/api/v1/plugins/transformation", + "projects/gloo/pkg/bootstrap", + "projects/gloo/pkg/defaults", ] - revision = "4f06039f2a3cf4ae684de8e854c5eefb6448177a" + pruneopts = "UT" + revision = "a04a4cccdb236d10b579aef4b7ec6809c2946abb" + version = "v0.7.6" + +[[projects]] + digest = "1:699918466a9758902a14923851a7fbd230500f93da6982dae7c79959afb0a664" + name = "github.com/solo-io/go-checkpoint" + packages = ["."] + pruneopts = "UT" + revision = "b798a7563f831f081f2a73ca889888f1ceec06b3" + version = "0.0.4" [[projects]] - name = "github.com/solo-io/glooctl" + digest = "1:88327fd7a42e10b263348fd2fac906e4f62df907c3bd2fdc33f9f9b927b94e2d" + name = "github.com/solo-io/go-utils" packages = [ - "pkg/config", - "pkg/util" + "cliutils", + "contextutils", + "errors", + "kubeutils", + "protoutils", + "stats", + "versionutils", ] - revision = "d46dc8570d2328f332540d7c63718e10d2701443" - version = "v0.2.8" + pruneopts = "UT" + revision = "dca6da0f663d404c2966e711c217e00679b1d0b2" + version = "v0.1.3" [[projects]] - branch = "master" - name = "github.com/solo-io/go-checkpoint" - packages = ["."] - revision = "69104b1493dcf66693f3832c9641cc38631bc129" + digest = "1:5072ecf183c1ec140aab6e49d084975f16d70fc74a15d68e6bb53e220ab20c75" + name = "github.com/solo-io/solo-kit" + packages = [ + "pkg/api/v1/clients", + "pkg/api/v1/clients/configmap", + "pkg/api/v1/clients/consul", + "pkg/api/v1/clients/factory", + "pkg/api/v1/clients/file", + "pkg/api/v1/clients/kube", + "pkg/api/v1/clients/kube/cache", + "pkg/api/v1/clients/kube/controller", + "pkg/api/v1/clients/kube/crd", + "pkg/api/v1/clients/kube/crd/client/clientset/versioned", + "pkg/api/v1/clients/kube/crd/client/clientset/versioned/scheme", + "pkg/api/v1/clients/kube/crd/client/clientset/versioned/typed/solo.io/v1", + "pkg/api/v1/clients/kube/crd/solo.io/v1", + "pkg/api/v1/clients/kubesecret", + "pkg/api/v1/clients/memory", + "pkg/api/v1/clients/vault", + "pkg/api/v1/control-plane/cache", + "pkg/api/v1/control-plane/log", + "pkg/api/v1/control-plane/server", + "pkg/api/v1/reconcile", + "pkg/api/v1/reporter", + "pkg/api/v1/resources", + "pkg/api/v1/resources/core", + "pkg/code-generator", + "pkg/code-generator/cmd", + "pkg/code-generator/codegen", + "pkg/code-generator/codegen/templates", + "pkg/code-generator/docgen", + "pkg/code-generator/docgen/funcs", + "pkg/code-generator/docgen/options", + "pkg/code-generator/docgen/templates/markdown", + "pkg/code-generator/docgen/templates/restructured", + "pkg/code-generator/model", + "pkg/code-generator/parser", + "pkg/errors", + "pkg/utils/cliutils", + "pkg/utils/contextutils", + "pkg/utils/errutils", + "pkg/utils/fileutils", + "pkg/utils/hashutils", + "pkg/utils/kubeutils", + "pkg/utils/log", + "pkg/utils/protoutils", + "pkg/utils/stringutils", + "test/helpers", + "test/setup", + "test/tests/typed", + ] + pruneopts = "UT" + revision = "73680f9c5837e9ea3e807d483f236f8080106d3e" + version = "v0.2.28" [[projects]] + digest = "1:e01b05ba901239c783dfe56450bcde607fc858908529868259c9a8765dc176d0" name = "github.com/spf13/cobra" - packages = ["."] + packages = [ + ".", + "doc", + ] + pruneopts = "UT" revision = "ef82de70bb3f60c65fb8eebacbb2d122ef517385" version = "v0.0.3" [[projects]] + digest = "1:9424f440bba8f7508b69414634aef3b2b3a877e522d8a4624692412805407bb7" name = "github.com/spf13/pflag" packages = ["."] + pruneopts = "UT" revision = "583c0c0531f06d5278b7d917446061adc344b5cd" version = "v1.0.1" [[projects]] - name = "github.com/ugorji/go" - packages = ["codec"] - revision = "b4c50a2b199d93b13dc15e78929cfb23bfdf21ab" - version = "v1.1.1" + digest = "1:9deeaaf623bd5bff1aff7317417ffcd960557afa3b1a1dc27ca800a416a4b6a3" + name = "github.com/technosophos/moniker" + packages = ["."] + pruneopts = "UT" + revision = "a5dbd03a2245d554160e3ae6bfdcf969fe58b431" + version = "0.2.0" [[projects]] - branch = "master" + digest = "1:768c1db4944fa5ab6944d11847200ba4b8b9e2b45458b7a20b4272fe599a1de0" name = "github.com/vektah/gqlgen" packages = [ "graphql", @@ -552,21 +1111,83 @@ "neelance/introspection", "neelance/query", "neelance/schema", - "neelance/validation" + "neelance/validation", ] + pruneopts = "UT" revision = "6b3b338d5f9c8b5a80ad4ea1e2e37aa58677ea9d" +[[projects]] + digest = "1:520b3fe2981935aec909ea78b4db76912e47235d9aea024212ffd11ae3b88dbb" + name = "go.opencensus.io" + packages = [ + ".", + "exporter/prometheus", + "internal", + "internal/tagencoding", + "plugin/ocgrpc", + "stats", + "stats/internal", + "stats/view", + "tag", + "trace", + "trace/internal", + "trace/propagation", + "trace/tracestate", + "zpages", + "zpages/internal", + ] + pruneopts = "UT" + revision = "b11f239c032624b045c4c2bfd3d1287b4012ce89" + version = "v0.16.0" + +[[projects]] + digest = "1:3c1a69cdae3501bf75e76d0d86dc6f2b0a7421bc205c0cb7b96b19eed464a34d" + name = "go.uber.org/atomic" + packages = ["."] + pruneopts = "UT" + revision = "1ea20fb1cbb1cc08cbd0d913a96dead89aa18289" + version = "v1.3.2" + +[[projects]] + digest = "1:60bf2a5e347af463c42ed31a493d817f8a72f102543060ed992754e689805d1a" + name = "go.uber.org/multierr" + packages = ["."] + pruneopts = "UT" + revision = "3c4937480c32f4c13a875a1829af76c98ca3d40a" + version = "v1.1.0" + +[[projects]] + digest = "1:d9a420eae5f76973feeb733fbf58f6a89173255b63b27a7ae4b2124a73dc6c5b" + name = "go.uber.org/zap" + packages = [ + ".", + "buffer", + "internal/bufferpool", + "internal/color", + "internal/exit", + "zapcore", + ] + pruneopts = "UT" + revision = "4d45f9617f7d90f7a663ff21c7a4321dbe78098b" + version = "v1.9.0" + [[projects]] branch = "master" + digest = "1:28c92854b02898c94c1d983db50970fb7d9b3ee31c3ae02b1c6afc665abcc46e" name = "golang.org/x/crypto" packages = [ - "blake2b", - "ssh/terminal" + "ed25519", + "ed25519/internal/edwards25519", + "pbkdf2", + "scrypt", + "ssh/terminal", ] - revision = "ab813273cd59e1333f7ae7bff5d027d4aadf528c" + pruneopts = "UT" + revision = "a2144134853fc9a27a7b1e3eb4f19f1a76df13c9" [[projects]] branch = "master" + digest = "1:f4a333ecb02eebcda25f0218fcb6aab7e67c76b0892d1ed505ecb784b1bd5121" name = "golang.org/x/net" packages = [ "context", @@ -579,33 +1200,46 @@ "http2/hpack", "idna", "internal/timeseries", - "trace" + "trace", ] - revision = "dfa909b99c79129e1100513e5cd36307665e5723" + pruneopts = "UT" + revision = "d0887baf81f4598189d4e12a37c6da86f0bba4d0" [[projects]] branch = "master" + digest = "1:f221a62ba33f5db9684d4f2c0519818fb8466c250aad1057d4e46d47450f3622" name = "golang.org/x/oauth2" packages = [ ".", "google", "internal", "jws", - "jwt" + "jwt", ] - revision = "ec22f46f877b4505e0117eeaab541714644fdd28" + pruneopts = "UT" + revision = "8f65e3013ebad444f13bc19536f7865efc793816" + +[[projects]] + branch = "master" + digest = "1:b521f10a2d8fa85c04a8ef4e62f2d1e14d303599a55d64dabf9f5a02f84d35eb" + name = "golang.org/x/sync" + packages = ["errgroup"] + pruneopts = "UT" + revision = "37e7f081c4d4c64e13b10787722085407fe5d15f" [[projects]] branch = "master" + digest = "1:3364d01296ce7eeca363e3d530ae63a2092d6f8efb85fb3d101e8f6d7de83452" name = "golang.org/x/sys" packages = [ - "cpu", "unix", - "windows" + "windows", ] - revision = "c11f84a56e43e20a78cee75a7c034031ecf57d1f" + pruneopts = "UT" + revision = "ac767d655b305d4e9612f5f6e33120b9176c4ad4" [[projects]] + digest = "1:bb8277a2ca2bcad6ff7f413b939375924099be908cedd1314baa21ecd08df477" name = "golang.org/x/text" packages = [ "collate", @@ -633,12 +1267,23 @@ "unicode/bidi", "unicode/cldr", "unicode/norm", - "unicode/rangetable" + "unicode/rangetable", + "width", ] + pruneopts = "UT" revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" version = "v0.3.0" [[projects]] + branch = "master" + digest = "1:c9e7a4b4d47c0ed205d257648b0e5b0440880cb728506e318f8ac7cd36270bc4" + name = "golang.org/x/time" + packages = ["rate"] + pruneopts = "UT" + revision = "fbb02b2291d28baffd63558aa44b4b56f178d650" + +[[projects]] + digest = "1:d2a8db567a76203e3b41c1f632d86485ffd57f8e650a0d1b19d240671c2fddd7" name = "google.golang.org/appengine" packages = [ ".", @@ -650,33 +1295,38 @@ "internal/modules", "internal/remote_api", "internal/urlfetch", - "urlfetch" + "urlfetch", ] - revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a" - version = "v1.0.0" + pruneopts = "UT" + revision = "4a4468ece617fc8205e99368fa2200e9d1fad421" + version = "v1.3.0" [[projects]] branch = "master" + digest = "1:601e63e7d4577f907118bec825902505291918859d223bce015539e79f1160e3" name = "google.golang.org/genproto" packages = ["googleapis/rpc/status"] - revision = "81158efcc9f219c511e4d3c0d61a0e6e49c01a24" + pruneopts = "UT" + revision = "02b4e95473316948020af0b7a4f0f22c73929b0e" [[projects]] + digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74" name = "google.golang.org/grpc" packages = [ ".", "balancer", "balancer/base", "balancer/roundrobin", - "channelz", "codes", "connectivity", "credentials", "encoding", "encoding/proto", - "grpclb/grpc_lb_v1/messages", "grpclog", "internal", + "internal/backoff", + "internal/channelz", + "internal/grpcrand", "keepalive", "metadata", "naming", @@ -687,79 +1337,140 @@ "stats", "status", "tap", - "transport" + "transport", ] - revision = "22a49e732a507a11ac524e4ea5fd1a3e384b6fb8" - version = "v1.12.1" + pruneopts = "UT" + revision = "168a6198bcb0ef175f7dacec0b8691fc141dc9b8" + version = "v1.13.0" [[projects]] + digest = "1:61e056dd0a189d9f1d07de809181f2ee5d36058d1ce4f9e58f443a1bea745ac0" + name = "gopkg.in/AlecAivazis/survey.v1" + packages = [ + ".", + "core", + "terminal", + ] + pruneopts = "UT" + revision = "ee72c28b95a5ff3e227cfd403f556f0ef4b07ca5" + version = "v1.8.2" + +[[projects]] + digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd" + name = "gopkg.in/fsnotify/fsnotify.v1" + packages = ["."] + pruneopts = "UT" + revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" + version = "v1.4.7" + +[[projects]] + digest = "1:2d1fbdc6777e5408cabeb02bf336305e724b925ff4546ded0fa8715a7267922a" name = "gopkg.in/inf.v0" packages = ["."] + pruneopts = "UT" revision = "d2d2541c53f18d2a059457998ce2876cc8e67cbf" version = "v0.9.1" [[projects]] + digest = "1:a4cde1eec9a17eb2399a50c6e1a9fe3fde039994de058f9dbf6592d157bfe97b" + name = "gopkg.in/square/go-jose.v2" + packages = [ + ".", + "cipher", + "json", + "jwt", + ] + pruneopts = "UT" + revision = "e94fb177d3668d35ab39c61cbb2f311550557e83" + version = "v2.2.2" + +[[projects]] + digest = "1:3c839a777de0e6da035c9de900b60cbec463b0a89351192c1ea083eaf9e0fce0" + name = "gopkg.in/tomb.v1" + packages = ["."] + pruneopts = "UT" + revision = "c131134a1947e9afd9cecfe11f4c6dff0732ae58" + +[[projects]] + digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202" name = "gopkg.in/yaml.v2" packages = ["."] + pruneopts = "UT" revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" version = "v2.2.1" [[projects]] - branch = "master" + digest = "1:a218faabd81ea62d514604e97d040b9c6d17ea0bbd5cf9a4e542d2d02f79512f" name = "k8s.io/api" packages = [ + "admission/v1beta1", "admissionregistration/v1alpha1", "admissionregistration/v1beta1", "apps/v1", "apps/v1beta1", "apps/v1beta2", + "auditregistration/v1alpha1", "authentication/v1", "authentication/v1beta1", "authorization/v1", "authorization/v1beta1", "autoscaling/v1", "autoscaling/v2beta1", + "autoscaling/v2beta2", "batch/v1", "batch/v1beta1", "batch/v2alpha1", "certificates/v1beta1", + "coordination/v1beta1", "core/v1", "events/v1beta1", "extensions/v1beta1", + "imagepolicy/v1alpha1", "networking/v1", "policy/v1beta1", "rbac/v1", "rbac/v1alpha1", "rbac/v1beta1", "scheduling/v1alpha1", + "scheduling/v1beta1", "settings/v1alpha1", "storage/v1", "storage/v1alpha1", - "storage/v1beta1" + "storage/v1beta1", ] - revision = "dc6e60c1b0483cd8e94610bfacf6989fe55df091" + pruneopts = "UT" + revision = "89a74a8d264df0e993299876a8cde88379b940ee" + version = "kubernetes-1.13.0" [[projects]] - branch = "master" + digest = "1:ace21b4261a9bc8d06b188f03ce48d1bed92ccba4890fad616cf283939f723c6" name = "k8s.io/apiextensions-apiserver" packages = [ "pkg/apis/apiextensions", "pkg/apis/apiextensions/v1beta1", "pkg/client/clientset/clientset", "pkg/client/clientset/clientset/scheme", - "pkg/client/clientset/clientset/typed/apiextensions/v1beta1" + "pkg/client/clientset/clientset/typed/apiextensions/v1beta1", + "pkg/features", ] - revision = "bd76ce7dd8e65e8c2197803a1e64869ca5905309" + pruneopts = "UT" + revision = "20c909e7c8c3fec1a0e345b1d4e57f1c1623c368" + version = "kubernetes-1.13.0" [[projects]] + digest = "1:6489d76eab90e477f61eb30186098b5d2e45acd08cefdfdc4988e30c3752ff30" name = "k8s.io/apimachinery" packages = [ + "pkg/api/equality", "pkg/api/errors", "pkg/api/meta", "pkg/api/resource", + "pkg/api/validation", "pkg/apis/meta/internalversion", "pkg/apis/meta/v1", "pkg/apis/meta/v1/unstructured", + "pkg/apis/meta/v1/unstructured/unstructuredscheme", + "pkg/apis/meta/v1/validation", "pkg/apis/meta/v1beta1", "pkg/conversion", "pkg/conversion/queryparams", @@ -778,28 +1489,113 @@ "pkg/util/cache", "pkg/util/clock", "pkg/util/diff", + "pkg/util/duration", "pkg/util/errors", "pkg/util/framer", + "pkg/util/httpstream", + "pkg/util/httpstream/spdy", "pkg/util/intstr", "pkg/util/json", + "pkg/util/mergepatch", + "pkg/util/naming", "pkg/util/net", + "pkg/util/rand", + "pkg/util/remotecommand", "pkg/util/runtime", "pkg/util/sets", + "pkg/util/strategicpatch", "pkg/util/validation", "pkg/util/validation/field", "pkg/util/wait", "pkg/util/yaml", "pkg/version", "pkg/watch", - "third_party/forked/golang/reflect" + "third_party/forked/golang/json", + "third_party/forked/golang/netutil", + "third_party/forked/golang/reflect", + ] + pruneopts = "UT" + revision = "2b1284ed4c93a43499e781493253e2ac5959c4fd" + version = "kubernetes-1.13.0" + +[[projects]] + digest = "1:138928c37ee6ec1be14aec46c97dab5f110ffa7e81828b9d27919df371fd8f62" + name = "k8s.io/apiserver" + packages = [ + "pkg/authentication/authenticator", + "pkg/authentication/serviceaccount", + "pkg/authentication/user", + "pkg/features", + "pkg/util/feature", + ] + pruneopts = "UT" + revision = "9caa0299108fbdf51d3d9b8e8956834ae84dac75" + version = "kubernetes-1.13.0" + +[[projects]] + branch = "release-1.12" + digest = "1:7991e5074de01462e0cf6ef77060895b50e9026d16152a6e925cb99b67a1f8ae" + name = "k8s.io/cli-runtime" + packages = [ + "pkg/genericclioptions", + "pkg/genericclioptions/printers", + "pkg/genericclioptions/resource", ] - revision = "31dade610c053669d8054bfd847da657251e8c1a" + pruneopts = "UT" + revision = "4c1cca29720474bad8854aa44b1dcb03d8c4e2b4" [[projects]] + digest = "1:bc9597aec563452a0bab34430ae11d6d6bc881e08d8d7d300de81f3f1d1ba620" name = "k8s.io/client-go" packages = [ "discovery", - "discovery/fake", + "dynamic", + "informers", + "informers/admissionregistration", + "informers/admissionregistration/v1alpha1", + "informers/admissionregistration/v1beta1", + "informers/apps", + "informers/apps/v1", + "informers/apps/v1beta1", + "informers/apps/v1beta2", + "informers/auditregistration", + "informers/auditregistration/v1alpha1", + "informers/autoscaling", + "informers/autoscaling/v1", + "informers/autoscaling/v2beta1", + "informers/autoscaling/v2beta2", + "informers/batch", + "informers/batch/v1", + "informers/batch/v1beta1", + "informers/batch/v2alpha1", + "informers/certificates", + "informers/certificates/v1beta1", + "informers/coordination", + "informers/coordination/v1beta1", + "informers/core", + "informers/core/v1", + "informers/events", + "informers/events/v1beta1", + "informers/extensions", + "informers/extensions/v1beta1", + "informers/internalinterfaces", + "informers/networking", + "informers/networking/v1", + "informers/policy", + "informers/policy/v1beta1", + "informers/rbac", + "informers/rbac/v1", + "informers/rbac/v1alpha1", + "informers/rbac/v1beta1", + "informers/scheduling", + "informers/scheduling/v1alpha1", + "informers/scheduling/v1beta1", + "informers/settings", + "informers/settings/v1alpha1", + "informers/storage", + "informers/storage/v1", + "informers/storage/v1alpha1", + "informers/storage/v1beta1", "kubernetes", "kubernetes/scheme", "kubernetes/typed/admissionregistration/v1alpha1", @@ -807,16 +1603,19 @@ "kubernetes/typed/apps/v1", "kubernetes/typed/apps/v1beta1", "kubernetes/typed/apps/v1beta2", + "kubernetes/typed/auditregistration/v1alpha1", "kubernetes/typed/authentication/v1", "kubernetes/typed/authentication/v1beta1", "kubernetes/typed/authorization/v1", "kubernetes/typed/authorization/v1beta1", "kubernetes/typed/autoscaling/v1", "kubernetes/typed/autoscaling/v2beta1", + "kubernetes/typed/autoscaling/v2beta2", "kubernetes/typed/batch/v1", "kubernetes/typed/batch/v1beta1", "kubernetes/typed/batch/v2alpha1", "kubernetes/typed/certificates/v1beta1", + "kubernetes/typed/coordination/v1beta1", "kubernetes/typed/core/v1", "kubernetes/typed/events/v1beta1", "kubernetes/typed/extensions/v1beta1", @@ -826,19 +1625,60 @@ "kubernetes/typed/rbac/v1alpha1", "kubernetes/typed/rbac/v1beta1", "kubernetes/typed/scheduling/v1alpha1", + "kubernetes/typed/scheduling/v1beta1", "kubernetes/typed/settings/v1alpha1", "kubernetes/typed/storage/v1", "kubernetes/typed/storage/v1alpha1", "kubernetes/typed/storage/v1beta1", + "listers/admissionregistration/v1alpha1", + "listers/admissionregistration/v1beta1", + "listers/apps/v1", + "listers/apps/v1beta1", + "listers/apps/v1beta2", + "listers/auditregistration/v1alpha1", + "listers/autoscaling/v1", + "listers/autoscaling/v2beta1", + "listers/autoscaling/v2beta2", + "listers/batch/v1", + "listers/batch/v1beta1", + "listers/batch/v2alpha1", + "listers/certificates/v1beta1", + "listers/coordination/v1beta1", + "listers/core/v1", + "listers/events/v1beta1", + "listers/extensions/v1beta1", + "listers/networking/v1", + "listers/policy/v1beta1", + "listers/rbac/v1", + "listers/rbac/v1alpha1", + "listers/rbac/v1beta1", + "listers/scheduling/v1alpha1", + "listers/scheduling/v1beta1", + "listers/settings/v1alpha1", + "listers/storage/v1", + "listers/storage/v1alpha1", + "listers/storage/v1beta1", + "pkg/apis/clientauthentication", + "pkg/apis/clientauthentication/v1alpha1", + "pkg/apis/clientauthentication/v1beta1", "pkg/version", "plugin/pkg/client/auth", "plugin/pkg/client/auth/azure", + "plugin/pkg/client/auth/exec", "plugin/pkg/client/auth/gcp", "plugin/pkg/client/auth/oidc", "plugin/pkg/client/auth/openstack", "rest", "rest/watch", - "testing", + "restmapper", + "scale", + "scale/scheme", + "scale/scheme/appsint", + "scale/scheme/appsv1beta1", + "scale/scheme/appsv1beta2", + "scale/scheme/autoscalingv1", + "scale/scheme/extensionsint", + "scale/scheme/extensionsv1beta1", "third_party/forked/golang/template", "tools/auth", "tools/cache", @@ -848,21 +1688,292 @@ "tools/clientcmd/api/v1", "tools/metrics", "tools/pager", + "tools/portforward", + "tools/record", "tools/reference", + "tools/remotecommand", + "tools/watch", "transport", + "transport/spdy", "util/buffer", "util/cert", + "util/connrotation", + "util/exec", "util/flowcontrol", "util/homedir", "util/integer", - "util/jsonpath" + "util/jsonpath", + "util/retry", + "util/workqueue", + ] + pruneopts = "UT" + revision = "e64494209f554a6723674bd494d69445fb76a1d4" + version = "kubernetes-1.13.0" + +[[projects]] + digest = "1:03170ab6a355ee2c646c063ae48e75ee206b42887e373ca58220ba0a88d9f6ba" + name = "k8s.io/helm" + packages = [ + "pkg/chartutil", + "pkg/engine", + "pkg/hooks", + "pkg/ignore", + "pkg/kube", + "pkg/manifest", + "pkg/proto/hapi/chart", + "pkg/proto/hapi/release", + "pkg/proto/hapi/rudder", + "pkg/proto/hapi/services", + "pkg/proto/hapi/version", + "pkg/releasetesting", + "pkg/releaseutil", + "pkg/renderutil", + "pkg/rudder", + "pkg/storage", + "pkg/storage/driver", + "pkg/storage/errors", + "pkg/sympath", + "pkg/tiller", + "pkg/tiller/environment", + "pkg/timeconv", + "pkg/version", ] - revision = "78700dec6369ba22221b72770783300f143df150" - version = "v6.0.0" + pruneopts = "UT" + revision = "eecf22f77df5f65c823aacd2dbd30ae6c65f186e" + version = "v2.12.3" + +[[projects]] + digest = "1:e2999bf1bb6eddc2a6aa03fe5e6629120a53088926520ca3b4765f77d7ff7eab" + name = "k8s.io/klog" + packages = ["."] + pruneopts = "UT" + revision = "a5bc97fbc634d635061f3146511332c7e313a55a" + version = "v0.1.0" + +[[projects]] + branch = "master" + digest = "1:e43c5c2646674fc5962f3833bc40f9b34868ba1bffd2a4fbd349cf340ebfff41" + name = "k8s.io/kube-openapi" + packages = [ + "pkg/util/proto", + "pkg/util/proto/validation", + ] + pruneopts = "UT" + revision = "ea82251f3668f8c1bde607fa6e20e5bf36e576a4" + +[[projects]] + digest = "1:e38c623dbc145357cd0f49822566ab0b6283b6c159c04cc36998a1fcbdd76bed" + name = "k8s.io/kubernetes" + packages = [ + "pkg/api/legacyscheme", + "pkg/api/service", + "pkg/api/v1/pod", + "pkg/apis/apps", + "pkg/apis/apps/install", + "pkg/apis/apps/v1", + "pkg/apis/apps/v1beta1", + "pkg/apis/apps/v1beta2", + "pkg/apis/authentication", + "pkg/apis/authentication/install", + "pkg/apis/authentication/v1", + "pkg/apis/authentication/v1beta1", + "pkg/apis/authorization", + "pkg/apis/authorization/install", + "pkg/apis/authorization/v1", + "pkg/apis/authorization/v1beta1", + "pkg/apis/autoscaling", + "pkg/apis/autoscaling/install", + "pkg/apis/autoscaling/v1", + "pkg/apis/autoscaling/v2beta1", + "pkg/apis/autoscaling/v2beta2", + "pkg/apis/batch", + "pkg/apis/batch/install", + "pkg/apis/batch/v1", + "pkg/apis/batch/v1beta1", + "pkg/apis/batch/v2alpha1", + "pkg/apis/certificates", + "pkg/apis/certificates/install", + "pkg/apis/certificates/v1beta1", + "pkg/apis/coordination", + "pkg/apis/coordination/install", + "pkg/apis/coordination/v1beta1", + "pkg/apis/core", + "pkg/apis/core/helper", + "pkg/apis/core/install", + "pkg/apis/core/pods", + "pkg/apis/core/v1", + "pkg/apis/core/v1/helper", + "pkg/apis/core/validation", + "pkg/apis/events", + "pkg/apis/events/install", + "pkg/apis/events/v1beta1", + "pkg/apis/extensions", + "pkg/apis/extensions/install", + "pkg/apis/extensions/v1beta1", + "pkg/apis/networking", + "pkg/apis/policy", + "pkg/apis/policy/install", + "pkg/apis/policy/v1beta1", + "pkg/apis/rbac", + "pkg/apis/rbac/install", + "pkg/apis/rbac/v1", + "pkg/apis/rbac/v1alpha1", + "pkg/apis/rbac/v1beta1", + "pkg/apis/scheduling", + "pkg/apis/scheduling/install", + "pkg/apis/scheduling/v1alpha1", + "pkg/apis/scheduling/v1beta1", + "pkg/apis/settings", + "pkg/apis/settings/install", + "pkg/apis/settings/v1alpha1", + "pkg/apis/storage", + "pkg/apis/storage/install", + "pkg/apis/storage/util", + "pkg/apis/storage/v1", + "pkg/apis/storage/v1alpha1", + "pkg/apis/storage/v1beta1", + "pkg/capabilities", + "pkg/controller", + "pkg/controller/deployment/util", + "pkg/features", + "pkg/fieldpath", + "pkg/kubectl/cmd/get", + "pkg/kubectl/cmd/util", + "pkg/kubectl/cmd/util/openapi", + "pkg/kubectl/cmd/util/openapi/validation", + "pkg/kubectl/generated", + "pkg/kubectl/scheme", + "pkg/kubectl/util/i18n", + "pkg/kubectl/util/printers", + "pkg/kubectl/util/templates", + "pkg/kubectl/util/term", + "pkg/kubectl/validation", + "pkg/kubelet/apis", + "pkg/kubelet/types", + "pkg/master/ports", + "pkg/printers", + "pkg/printers/internalversion", + "pkg/scheduler/api", + "pkg/security/apparmor", + "pkg/serviceaccount", + "pkg/util/file", + "pkg/util/hash", + "pkg/util/interrupt", + "pkg/util/labels", + "pkg/util/net/sets", + "pkg/util/node", + "pkg/util/parsers", + "pkg/util/taints", + "pkg/version", + ] + pruneopts = "UT" + revision = "ddf47ac13c1a9483ea035a79cd7c10005ff21a6d" + version = "v1.13.0" + +[[projects]] + branch = "master" + digest = "1:c8576f66d94cb34b91fbe8a53df9c14fd741cc747795e294fe01efe0b7c73c93" + name = "k8s.io/utils" + packages = [ + "exec", + "pointer", + ] + pruneopts = "UT" + revision = "cdba02414f767c89d729b1b9e61d9891f4e82b35" + +[[projects]] + digest = "1:7719608fe0b52a4ece56c2dde37bedd95b938677d1ab0f84b8a7852e4c59f849" + name = "sigs.k8s.io/yaml" + packages = ["."] + pruneopts = "UT" + revision = "fd68e9863619f6ec2fdd8625fe1f02e7c877e480" + version = "v1.1.0" + +[[projects]] + branch = "master" + digest = "1:9132eacc44d9bd1e03145ea2e9d4888800da7773d6edebb401f8cd34c9fb8380" + name = "vbom.ml/util" + packages = ["sortorder"] + pruneopts = "UT" + revision = "efcd4e0f97874370259c7d93e12aad57911dea81" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "6ca1505fc6c92c39a50dc422c603353cd5806e5ef736e4b86d613a6c119f35b9" + input-imports = [ + "github.com/fatih/structs", + "github.com/ghodss/yaml", + "github.com/gogo/protobuf/gogoproto", + "github.com/gogo/protobuf/proto", + "github.com/gogo/protobuf/types", + "github.com/gorilla/mux", + "github.com/hashicorp/go-multierror", + "github.com/ilackarms/protoc-gen-doc", + "github.com/onsi/ginkgo", + "github.com/onsi/gomega", + "github.com/pelletier/go-toml", + "github.com/pkg/errors", + "github.com/solo-io/gloo/install/helm/gloo/generate", + "github.com/solo-io/gloo/pkg/cliutil", + "github.com/solo-io/gloo/pkg/cliutil/install", + "github.com/solo-io/gloo/pkg/utils/setuputils", + "github.com/solo-io/gloo/projects/gloo/cli/pkg/constants", + "github.com/solo-io/gloo/projects/gloo/cli/pkg/helpers", + "github.com/solo-io/gloo/projects/gloo/pkg/api/v1", + "github.com/solo-io/gloo/projects/gloo/pkg/api/v1/plugins/rest", + "github.com/solo-io/gloo/projects/gloo/pkg/bootstrap", + "github.com/solo-io/go-checkpoint", + "github.com/solo-io/go-utils/cliutils", + "github.com/solo-io/go-utils/errors", + "github.com/solo-io/go-utils/kubeutils", + "github.com/solo-io/go-utils/stats", + "github.com/solo-io/go-utils/versionutils", + "github.com/solo-io/solo-kit/pkg/api/v1/clients", + "github.com/solo-io/solo-kit/pkg/api/v1/clients/factory", + "github.com/solo-io/solo-kit/pkg/api/v1/clients/kube", + "github.com/solo-io/solo-kit/pkg/api/v1/clients/kube/crd", + "github.com/solo-io/solo-kit/pkg/api/v1/clients/memory", + "github.com/solo-io/solo-kit/pkg/api/v1/reconcile", + "github.com/solo-io/solo-kit/pkg/api/v1/reporter", + "github.com/solo-io/solo-kit/pkg/api/v1/resources", + "github.com/solo-io/solo-kit/pkg/api/v1/resources/core", + "github.com/solo-io/solo-kit/pkg/code-generator/cmd", + "github.com/solo-io/solo-kit/pkg/code-generator/docgen/options", + "github.com/solo-io/solo-kit/pkg/errors", + "github.com/solo-io/solo-kit/pkg/utils/contextutils", + "github.com/solo-io/solo-kit/pkg/utils/errutils", + "github.com/solo-io/solo-kit/pkg/utils/hashutils", + "github.com/solo-io/solo-kit/pkg/utils/kubeutils", + "github.com/solo-io/solo-kit/pkg/utils/log", + "github.com/solo-io/solo-kit/test/helpers", + "github.com/solo-io/solo-kit/test/setup", + "github.com/solo-io/solo-kit/test/tests/typed", + "github.com/spf13/cobra", + "github.com/spf13/cobra/doc", + "github.com/spf13/pflag", + "github.com/vektah/gqlgen/graphql", + "github.com/vektah/gqlgen/handler", + "github.com/vektah/gqlgen/neelance/common", + "github.com/vektah/gqlgen/neelance/introspection", + "github.com/vektah/gqlgen/neelance/query", + "github.com/vektah/gqlgen/neelance/schema", + "go.opencensus.io/stats", + "go.opencensus.io/stats/view", + "go.opencensus.io/tag", + "go.opencensus.io/trace", + "go.uber.org/zap", + "k8s.io/api/core/v1", + "k8s.io/apimachinery/pkg/api/errors", + "k8s.io/apimachinery/pkg/apis/meta/v1", + "k8s.io/apimachinery/pkg/runtime", + "k8s.io/apimachinery/pkg/runtime/schema", + "k8s.io/client-go/kubernetes", + "k8s.io/client-go/plugin/pkg/client/auth", + "k8s.io/client-go/plugin/pkg/client/auth/gcp", + "k8s.io/client-go/rest", + "k8s.io/helm/pkg/chartutil", + "k8s.io/helm/pkg/renderutil", + "sigs.k8s.io/yaml", + ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index d840cf9..d6d1176 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -1,53 +1,70 @@ -# Gopkg.toml example -# -# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[constraint]] -# name = "github.com/user/project2" -# branch = "dev" -# source = "github.com/myfork/project2" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" -# -# [prune] -# non-go = false -# go-tests = true -# unused-packages = true +[prune] + go-tests = true + unused-packages = true + +# used for older sqoop, remove if upgrading to 99designs +[[constraint]] + revision = "6b3b338d5f9c8b5a80ad4ea1e2e37aa58677ea9d" + name = "github.com/vektah/gqlgen" + +[[constraint]] + version = "0.7.2" + name = "github.com/99designs/gqlgen" + +[[constraint]] + version = "1.1.1" + name = "github.com/gogo/protobuf" [[override]] - name = "k8s.io/apimachinery" - revision = "31dade610c053669d8054bfd847da657251e8c1a" + name = "k8s.io/kubernetes" + version = "=v1.13.0" [[override]] - name = "github.com/Azure/go-autorest" - revision = "e14a70c556c8e0db173358d1a903dca345a8e75e" + name = "k8s.io/api" + version = "kubernetes-1.13.0" [[constraint]] - name = "github.com/solo-io/gloo" - revision = "4f06039f2a3cf4ae684de8e854c5eefb6448177a" + name = "k8s.io/client-go" + version = "kubernetes-1.13.0" + +[[constraint]] + name = "k8s.io/apimachinery" + version = "kubernetes-1.13.0" + +[[override]] + name = "k8s.io/apiextensions-apiserver" + version = "kubernetes-1.13.0" + +[[override]] + name = "k8s.io/apiserver" + version = "kubernetes-1.13.0" + +[[override]] + version = "1.2.0" + name = "github.com/golang/protobuf" + [[constraint]] - name = "github.com/onsi/ginkgo" - version = "1.5.0" + name = "github.com/solo-io/solo-kit" + version = "0.2.28" [[constraint]] - name = "github.com/onsi/gomega" - version = "1.4.0" + name = "github.com/solo-io/gloo" + version = "0.7.6" + +[[override]] + name = "github.com/hashicorp/consul" + revision = "eba25a143821cec25bdb48fe64dd7b7a6152d01d" + +# go versioning story not ideal, so we have to pin serf to the version consul expects +[[override]] + name = "github.com/hashicorp/serf" + revision = "19bbd39e421bdf3559d5025fb2c760f5ffa56233" [[constraint]] - branch = "master" - name = "github.com/vektah/gqlgen" + name = "github.com/solo-io/go-checkpoint" + version = "0.0.4" -[prune] - go-tests = true - unused-packages = true +[[constraint]] + name = "github.com/solo-io/go-utils" + version = "0.1.3" diff --git a/Makefile b/Makefile index f8375bb..522cddd 100644 --- a/Makefile +++ b/Makefile @@ -1,169 +1,228 @@ -# Change this if your googleapis is in a different directory -export GOOGLE_PROTOS_HOME=$(HOME)/workspace/googleapis +#---------------------------------------------------------------------------------- +# Base +#---------------------------------------------------------------------------------- ROOTDIR := $(shell pwd) -PROTOS := $(shell find api/v1 -name "*.proto") -SOURCES := $(shell find . -name "*.go" | grep -v test) -GENERATED_PROTO_FILES := $(shell find pkg/api/types/v1 -name "*.pb.go") -OUTPUT_DIR ?= _output - -PACKAGE_PATH:=github.com/solo-io/sqoop +OUTPUT_DIR ?= $(ROOTDIR)/_output +SOURCES := $(shell find . -name "*.go" | grep -v test.go | grep -v '\.\#*') +RELEASE := "true" +ifeq ($(TAGGED_VERSION),) + # TAGGED_VERSION := $(shell git describe --tags) + # This doesn't work in CI, need to find another way... + TAGGED_VERSION := vdev + RELEASE := "false" +endif +VERSION ?= $(shell echo $(TAGGED_VERSION) | cut -c 2-) + +LDFLAGS := "-X github.com/solo-io/sqoop/version.Version=$(VERSION)" +GCFLAGS := all="-N -l" #---------------------------------------------------------------------------------- -# Build +# Repo setup #---------------------------------------------------------------------------------- -# Generated code +# https://www.viget.com/articles/two-ways-to-share-git-hooks-with-your-team/ +.PHONY: init +init: + git config core.hooksPath .githooks -.PHONY: all -all: build +.PHONY: update-deps +update-deps: + go get -u golang.org/x/tools/cmd/goimports + go get -u github.com/gogo/protobuf/gogoproto + go get -u github.com/gogo/protobuf/protoc-gen-gogo + go get -u github.com/lyft/protoc-gen-validate + go get -u github.com/paulvollmer/2gobytes -.PHONY: proto -proto: $(GENERATED_PROTO_FILES) +.PHONY: pin-repos +pin-repos: + go run pin_repos.go -$(GENERATED_PROTO_FILES): $(PROTOS) - cd api/v1/ && \ - mkdir -p $(ROOTDIR)/pkg/api/types/v1 && \ - protoc \ - -I=. \ - -I=$(GOPATH)/src \ - -I=$(GOPATH)/src/github.com/gogo/protobuf/ \ - --gogo_out=$(GOPATH)/src \ - ./*.proto +.PHONY: check-format +check-format: + NOT_FORMATTED=$$(gofmt -l ./pkg/ ./test/) && if [ -n "$$NOT_FORMATTED" ]; then echo These files are not formatted: $$NOT_FORMATTED; exit 1; fi -$(OUTPUT_DIR): - mkdir -p $@ +check-spelling: + ./ci/spell.sh check -# kubernetes custom clientsets -.PHONY: clientset -clientset: $(GENERATED_PROTO_FILES) $(SOURCES) - cd ${GOPATH}/src/k8s.io/code-generator && \ - ./generate-groups.sh all \ - $(PACKAGE_PATH)/pkg/storage/crd/client \ - $(PACKAGE_PATH)/pkg/storage/crd \ - "solo.io:v1" .PHONY: generated-code -generated-code: +generated-code: $(OUTPUT_DIR)/.generated-code + +SUBDIRS:=pkg cmd +$(OUTPUT_DIR)/.generated-code: go generate ./... + (rm docs/cli/sqoopctl* && go run cli/cmd/docs/main.go) + gofmt -w $(SUBDIRS) + goimports -w $(SUBDIRS) + mkdir -p $(OUTPUT_DIR) + touch $@ -# Core Binaries +#---------------------------------------------------------------------------------- +# Clean +#---------------------------------------------------------------------------------- -BINARIES ?= sqoop -DEBUG_BINARIES = $(foreach BINARY,$(BINARIES),$(BINARY)-debug) +# Important to clean before pushing new releases. Dockerfiles and binaries may not update properly +.PHONY: clean +clean: + rm -rf _output + rm -fr site -DOCKER_ORG=soloio -.PHONY: build -build: $(BINARIES) +#---------------------------------------------------------------------------------- +# sqoop +#---------------------------------------------------------------------------------- -.PHONY: debug-build -debug-build: $(DEBUG_BINARIES) +SQOOP_DIR=. +SQOOP_SOURCES=$(call get_sources,$(SQOOP_DIR)) -docker: $(foreach BINARY,$(BINARIES),$(shell echo $(BINARY)-docker)) -docker-push: $(foreach BINARY,$(BINARIES),$(shell echo $(BINARY)-docker-push)) +$(OUTPUT_DIR)/sqoop-linux-amd64: $(SQOOP_SOURCES) + CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build -ldflags=$(LDFLAGS) -gcflags=$(GCFLAGS) -o $@ $(SQOOP_DIR)/cmd/main.go -define BINARY_TARGETS -$(eval VERSION := $(shell cat version)) -$(eval IMAGE_TAG ?= $(VERSION)) -$(eval OUTPUT_BINARY := $(OUTPUT_DIR)/$(BINARY)) -.PHONY: $(BINARY) -.PHONY: $(BINARY)-debug -.PHONY: $(BINARY)-docker -.PHONY: $(BINARY)-docker-debug -.PHONY: $(BINARY)-docker-push -.PHONY: $(BINARY)-docker-push-debug +.PHONY: sqoop +sqoop: $(OUTPUT_DIR)/sqoop-linux-amd64 -# nice targets for the binaries -$(BINARY): $(OUTPUT_BINARY) -$(BINARY)-debug: $(OUTPUT_BINARY)-debug +$(OUTPUT_DIR)/Dockerfile.sqoop: $(SQOOP_DIR)/cmd/Dockerfile + cp $< $@ -# go build -$(OUTPUT_BINARY): $(OUTPUT_DIR) $(PREREQUISITES) - CGO_ENABLED=0 GOOS=linux go build -v -o $(OUTPUT_BINARY) cmd/$(BINARY)/main.go -$(OUTPUT_BINARY)-debug: $(OUTPUT_DIR) $(PREREQUISITES) - go build -i -gcflags "all=-N -l" -o $(OUTPUT_BINARY)-debug cmd/$(BINARY)/main.go +sqoop-docker: $(OUTPUT_DIR)/sqoop-linux-amd64 $(OUTPUT_DIR)/Dockerfile.sqoop + docker build -t soloio/sqoop:$(VERSION) $(OUTPUT_DIR) -f $(OUTPUT_DIR)/Dockerfile.sqoop -# docker -$(BINARY)-docker: $(OUTPUT_BINARY) - docker build -t $(DOCKER_ORG)/$(BINARY):$(IMAGE_TAG) $(OUTPUT_DIR) -f - < cmd/$(BINARY)/Dockerfile -$(BINARY)-docker-debug: $(OUTPUT_BINARY)-debug - docker build -t $(DOCKER_ORG)/$(BINARY)-debug:$(IMAGE_TAG) $(OUTPUT_DIR) -f - < cmd/$(BINARY)/Dockerfile.debug -$(BINARY)-docker-push: $(BINARY)-docker - docker push $(DOCKER_ORG)/$(BINARY):$(IMAGE_TAG) -$(BINARY)-docker-push-debug: $(BINARY)-docker-debug - docker push $(DOCKER_ORG)/$(BINARY)-debug:$(IMAGE_TAG) +#---------------------------------------------------------------------------------- +# Deployment Manifests / Helm +#---------------------------------------------------------------------------------- -endef -PREREQUISITES := $(SOURCES) $(GENERATED_PROTO_FILES) generated-code clientset -$(foreach BINARY,$(BINARIES),$(eval $(BINARY_TARGETS))) +HELM_SYNC_DIR := $(OUTPUT_DIR)/helm +HELM_DIR := install/helm +MANIFEST_DIR := install/manifest -# clean +.PHONY: manifest +manifest: helm-template init-helm install/manifest/sqoop.yaml update-helm-chart -clean: - rm -rf $(OUTPUT_DIR) +# creates Chart.yaml, values.yaml, and requirements.yaml +.PHONY: helm-template +helm-template: + mkdir -p $(MANIFEST_DIR) + go run install/helm/sqoop/generate.go $(VERSION) + +update-helm-chart: +ifeq ($(RELEASE),"true") + mkdir -p $(HELM_SYNC_DIR)/charts + helm package --destination $(HELM_SYNC_DIR)/charts $(HELM_DIR)/sqoop + helm repo index $(HELM_SYNC_DIR) +endif + +install/manifest/sqoop.yaml: helm-template + helm template install/helm/sqoop --namespace gloo-system --name=sqoop > $@ + +init-helm: + helm repo add gloo https://storage.googleapis.com/solo-public-helm + helm dependency update install/helm/sqoop #---------------------------------------------------------------------------------- # sqoopctl #---------------------------------------------------------------------------------- -.PHONY: sqoopctl -sqoopctl: $(OUTPUT_DIR)/sqoopctl +CLI_DIR=cli $(OUTPUT_DIR)/sqoopctl: $(SOURCES) - go build -v -o $@ cmd/sqoopctl/main.go + go build -ldflags=$(LDFLAGS) -gcflags=$(GCFLAGS) -o $@ $(CLI_DIR)/cmd/main.go -$(OUTPUT_DIR)/sqoopctl-linux-amd64: $(SOURCES) - GOOS=linux go build -v -o $@ cmd/sqoopctl/main.go - -$(OUTPUT_DIR)/sqoopctl-darwin-amd64: $(SOURCES) - GOOS=darwin go build -v -o $@ cmd/sqoopctl/main.go -#---------------------------------------------------------------------------------- -# Docs -#---------------------------------------------------------------------------------- +$(OUTPUT_DIR)/sqoopctl-linux-amd64: $(SOURCES) + CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build -ldflags=$(LDFLAGS) -gcflags=$(GCFLAGS) -o $@ $(CLI_DIR)/cmd/main.go -docs/api.json: $(PROTOS) - export DISABLE_SORT=1 && \ - cd api/v1/ && \ - mkdir -p $(ROOTDIR)/pkg/api/types/v1 && \ - protoc \ - -I=. \ - -I=$(GOPATH)/src \ - -I=$(GOPATH)/src/github.com/gogo/protobuf/ \ - --plugin=protoc-gen-doc=$(GOPATH)/bin/protoc-gen-doc \ - --doc_out=$(ROOTDIR)/docs/ \ - --doc_opt=json,api.json \ - ./*.proto -docs/index.md: README.md - cat README.md | sed 's@docs/@@' > docs/index.md +$(OUTPUT_DIR)/sqoopctl-darwin-amd64: $(SOURCES) + CGO_ENABLED=0 GOARCH=amd64 GOOS=darwin go build -ldflags=$(LDFLAGS) -gcflags=$(GCFLAGS) -o $@ $(CLI_DIR)/cmd/main.go -docs/getting_started/kubernetes/1.md: examples/petstore/README.md - mkdir -p docs/getting_started/kubernetes/ - cp examples/petstore/README.md $@ +$(OUTPUT_DIR)/sqoopctl-windows-amd64.exe: $(SOURCES) + CGO_ENABLED=0 GOARCH=amd64 GOOS=windows go build -ldflags=$(LDFLAGS) -gcflags=$(GCFLAGS) -o $@ $(CLI_DIR)/cmd/main.go -doc: docs/api.json docs/index.md docs/getting_started/kubernetes/1.md - go run docs/gen_docs.go -site: doc - mkdocs build +.PHONY: sqoopctl +sqoopctl: $(OUTPUT_DIR)/sqoopctl +.PHONY: sqoopctl-linux-amd64 +sqoopctl-linux-amd64: $(OUTPUT_DIR)/sqoopctl-linux-amd64 +.PHONY: sqoopctl-darwin-amd64 +sqoopctl-darwin-amd64: $(OUTPUT_DIR)/sqoopctl-darwin-amd64 +.PHONY: sqoopctl-windows-amd64 +sqoopctl-windows-amd64: $(OUTPUT_DIR)/sqoopctl-windows-amd64.exe -deploy-site: site - firebase deploy --only hosting:sqoop-site #---------------------------------------------------------------------------------- # Release #---------------------------------------------------------------------------------- - -RELEASE_BINARIES := $(OUTPUT_DIR)/sqoopctl-linux-amd64 $(OUTPUT_DIR)/sqoopctl-darwin-amd64 +GH_ORG:=solo-io +GH_REPO:=sqoop + +# For now, expecting people using the release to start from a sqoopctl CLI we provide, not +# installing the binaries locally / directly. So only uploading the CLI binaries to Github. +# The other binaries can be built manually and used, and docker images for everything will +# be published on release. +RELEASE_BINARIES := +ifeq ($(RELEASE),"true") + RELEASE_BINARIES := \ + $(OUTPUT_DIR)/sqoopctl-linux-amd64 \ + $(OUTPUT_DIR)/sqoopctl-darwin-amd64 \ + $(OUTPUT_DIR)/sqoopctl-windows-amd64.exe +endif + +RELEASE_YAMLS := +ifeq ($(RELEASE),"true") + RELEASE_YAMLS := \ + install/manifest/sqoop.yaml +endif .PHONY: release-binaries release-binaries: $(RELEASE_BINARIES) +.PHONY: release-yamls +release-yamls: $(RELEASE_YAMLS) + +# This is invoked by cloudbuild. When the bot gets a release notification, it kicks of a build with and provides a tag +# variable that gets passed through to here as $TAGGED_VERSION. If no tag is provided, this is a no-op. If a tagged +# version is provided, all the release binaries are uploaded to github. +# Create new releases by clicking "Draft a new release" from https://github.com/solo-io/sqoop/releases .PHONY: release -release: release-binaries - hack/create-release.sh github_api_token=$(GITHUB_TOKEN) owner=solo-io repo=sqoop tag=v$(VERSION) - @$(foreach BINARY,$(RELEASE_BINARIES),hack/upload-github-release-asset.sh github_api_token=$(GITHUB_TOKEN) owner=solo-io repo=sqoop tag=v$(VERSION) filename=$(BINARY);) +release: release-binaries release-yamls +ifeq ($(RELEASE),"true") + ci/push-docs.sh tag=$(TAGGED_VERSION) + @$(foreach BINARY,$(RELEASE_BINARIES),ci/upload-github-release-asset.sh owner=solo-io repo=sqoop tag=$(TAGGED_VERSION) filename=$(BINARY) sha=TRUE;) + @$(foreach YAML,$(RELEASE_YAMLS),ci/upload-github-release-asset.sh owner=solo-io repo=sqoop tag=$(TAGGED_VERSION) filename=$(YAML);) +endif + +.PHONY: push-docs +push-docs: +ifeq ($(RELEASE),"true") + ci/push-docs.sh tag=$(TAGGED_VERSION) +endif + + +#---------------------------------------------------------------------------------- +# Docker +#---------------------------------------------------------------------------------- +# +#--------- +#--------- Push +#--------- + +DOCKER_IMAGES := +ifeq ($(RELEASE),"true") + DOCKER_IMAGES := docker +endif + +.PHONY: docker docker-push +docker: sqoop-docker + +# Depends on DOCKER_IMAGES, which is set to docker if RELEASE is "true", otherwise empty (making this a no-op). +# This prevents executing the dependent targets if RELEASE is not true, while still enabling `make docker` +# to be used for local testing. +# docker-push is intended to be run by CI +docker-push: $(DOCKER_IMAGES) +ifeq ($(RELEASE),"true") + docker push soloio/sqoop:$(VERSION) +endif + diff --git a/api/v1/config.proto b/api/v1/config.proto deleted file mode 100644 index 2940737..0000000 --- a/api/v1/config.proto +++ /dev/null @@ -1,17 +0,0 @@ -syntax = "proto3"; -package sqoop.api.v1; -option go_package = "github.com/solo-io/sqoop/pkg/api/types/v1"; - -import "schema.proto"; -import "resolver_map.proto"; - -import "gogoproto/gogo.proto"; -option (gogoproto.equal_all) = true; - -/** - * Config is a top-level config object. It is used internally by Sqoop as a container for the entire set of config objects. - */ -message Config { - repeated Schema schemas = 3; // the set of all schemas defined by the user - repeated ResolverMap resolver_maps = 4; // the set of all resolver maps defined by the user -} \ No newline at end of file diff --git a/api/v1/resolver_map.proto b/api/v1/resolver_map.proto index 8f88bd5..7cb5d29 100644 --- a/api/v1/resolver_map.proto +++ b/api/v1/resolver_map.proto @@ -1,13 +1,14 @@ syntax = "proto3"; -package sqoop.api.v1; -option go_package = "github.com/solo-io/sqoop/pkg/api/types/v1"; +package sqoop.solo.io; +option go_package = "github.com/solo-io/sqoop/pkg/api/v1"; import "gogoproto/gogo.proto"; option (gogoproto.equal_all) = true; -// imported from Gloo -import "github.com/solo-io/gloo/api/v1/status.proto"; -import "github.com/solo-io/gloo/api/v1/metadata.proto"; +import "github.com/solo-io/solo-kit/api/v1/metadata.proto"; +import "github.com/solo-io/solo-kit/api/v1/status.proto"; +import "github.com/solo-io/solo-kit/api/v1/solo-kit.proto"; +import "github.com/solo-io/gloo/projects/gloo/api/v1/proxy.proto"; /* * The ResolverMap object maps Resolvers to the fields in the GraphQL Schema @@ -15,32 +16,30 @@ import "github.com/solo-io/gloo/api/v1/metadata.proto"; * is read or updated if one does not alreay exist. */ message ResolverMap { - // Name of the Resolver Map. Resolver Map names must be unique - // - // Resolver Map Names must be unique and follow the following syntax rules: - // One or more lowercase rfc1035/rfc1123 labels separated by '.' with a maximum length of 253 characters. - string name = 1; + + option (core.solo.io.resource).short_name = "rm"; + option (core.solo.io.resource).plural_name = "resolver_maps"; // Types is a map of Type Names (defined in the schema) to a TypeResolver, which contain resolvers for the // specific fields of the type map types = 3; - // Status indicates the validation status of the role resource. + // Status indicates the validation status of this resource. // Status is read-only by clients, and set by gloo during validation - gloo.api.v1.Status status = 4 [(gogoproto.moretags) = "testdiff:\"ignore\""]; + core.solo.io.Status status = 6 [(gogoproto.nullable) = false, (gogoproto.moretags) = "testdiff:\"ignore\""]; - // Metadata contains the resource metadata for the role - gloo.api.v1.Metadata metadata = 5; + // Metadata contains the object metadata for this resource + core.solo.io.Metadata metadata = 7 [(gogoproto.nullable) = false]; } // TypeResolver contains the individual resolvers for each field for a specific type message TypeResolver { // This is a map of Field Names to the resolver that Sqoop should invoke when a query arrives for that field - map fields = 1; + map fields = 1; } // Resolvers define the actual logic Sqoop needs to know in order to resolve a specific field query -message Resolver { +message FieldResolver { // a resolver can have one of three types: oneof resolver { // a GlooResolver, which leverages Gloo to retrieve data from backend services and functions for the query @@ -55,49 +54,34 @@ message Resolver { // GlooResolvers are the "meat" of Sqoop. GlooResolvers tell Sqoop how to invoke a "Gloo Function" message GlooResolver { // the Request Template, if specified, will become the body of the HTTP request used to invoke a function through Gloo - // input parameters, if needed, should be specified in the request template. See Sqoop's [Resolver documentation](TODO) + // input parameters, if needed, should be specified in the request template. See Sqoop's [Resolver documentation] // for more information on Request Templates. - string request_template = 1; + RequestTemplate request_template = 1; // The response template, if specified, will transform the body of HTTP responses returned by Gloo functions. // This field should be used if the object returned by the Gloo Function does not match the type specified in the GraphQL schema. - // It can also be used to modify or transform responses from their original state. See Sqoop's [Resolver documentation](TODO) + // It can also be used to modify or transform responses from their original state. See Sqoop's [Resolver documentation] // for more information on Response Templates. - string response_template = 2; - // Optional. Use to set the outbound HTTP Request header `Content-Type`. Defaults to `application/json` - string content_type = 3; - // Specify the function the resolver should invoke. The function must be one registered to Gloo. - oneof function { - // SingleFunction specifies this resolver will always invoke a single function. - Function single_function = 4; - // MultiFunction specifies the resolver will distribute invocation across multiple functions - MultiFunction multi_function = 5; - } -} + ResponseTemplate response_template = 2; -// A reference to a function known to Gloo -message Function { - // Name of the Gloo Upstream that provides this function - string upstream = 1; - // Name of the function itself. See Gloo documentation for more details on functions in Gloo - string function = 2; + // the routing action to take when resolver is executed. usually this is a Route destination + gloo.solo.io.RouteAction action = 4; } -// A reference to a list of functions known to Gloo -message MultiFunction { - // A list of functions with weights. Must have size >= 1 - repeated WeightedFunction weighted_functions = 1; +message RequestTemplate { + string verb = 1; + string path = 2; + string body = 3; + map headers = 4; } -message WeightedFunction { - // the function to call - Function function = 1; - // Invoking each functoion will be balanced by the ratio of the function's weight to the total weight on a resolver - uint32 weight = 2; +message ResponseTemplate { + string body = 2; + map headers = 3; } // A Go-template which will return data for a Resolver without making a function call. Template Resolvers can make use // of Sqoop's builtin template functions as well as the data provided by the Params object to the resolver. -// Read more about Templates and Resolvers in Sqoop's [Resolver documentation](TODO). +// Read more about Templates and Resolvers in Sqoop\'s [Resolver documentation]. message TemplateResolver { // the Go template as an inline string string inline_template = 1; diff --git a/api/v1/schema.proto b/api/v1/schema.proto index 340ef2d..5ac8810 100644 --- a/api/v1/schema.proto +++ b/api/v1/schema.proto @@ -1,34 +1,33 @@ syntax = "proto3"; -package sqoop.api.v1; -option go_package = "github.com/solo-io/sqoop/pkg/api/types/v1"; +package sqoop.solo.io; +option go_package = "github.com/solo-io/sqoop/pkg/api/v1"; import "gogoproto/gogo.proto"; option (gogoproto.equal_all) = true; -// imported from Gloo -import "github.com/solo-io/gloo/api/v1/status.proto"; -import "github.com/solo-io/gloo/api/v1/metadata.proto"; +import "github.com/solo-io/solo-kit/api/v1/metadata.proto"; +import "github.com/solo-io/solo-kit/api/v1/status.proto"; +import "github.com/solo-io/solo-kit/api/v1/solo-kit.proto"; /* - * The Schema object wraps the user's GraphQL Schema, which is stored as an inline string. - * The Schema Object contains a Status field which is used by Sqoop to validate the user's input schema. + +* The Schema object wraps the user's GraphQL Schema, which is stored as an inline string. +* The Schema Object contains a Status field which is used by SQooP to validate the user's input schema. + +* Schemas are matched to resolver maps in the same namespace with the same name */ message Schema { - // Schema Names must be unique and follow the following syntax rules: - // One or more lowercase rfc1035/rfc1123 labels separated by '.' with a maximum length of 253 characters. - string name = 1; - // name of the resolver map to use to resolve this schema. - // if the user leaves this empty, Sqoop will generate the skeleton of a resolver map for the user - string resolver_map = 2; + option (core.solo.io.resource).short_name = "sc"; + option (core.solo.io.resource).plural_name = "schemas"; // inline the entire graphql schema as a string here string inline_schema = 3; - // Status indicates the validation status of the role resource. + // Status indicates the validation status of this resource. // Status is read-only by clients, and set by gloo during validation - gloo.api.v1.Status status = 6 [(gogoproto.moretags) = "testdiff:\"ignore\""]; + core.solo.io.Status status = 6 [(gogoproto.nullable) = false, (gogoproto.moretags) = "testdiff:\"ignore\""]; - // Metadata contains the resource metadata for the role - gloo.api.v1.Metadata metadata = 7; + // Metadata contains the object metadata for this resource + core.solo.io.Metadata metadata = 7 [(gogoproto.nullable) = false]; } diff --git a/api/v1/solo-kit.json b/api/v1/solo-kit.json new file mode 100644 index 0000000..ff07068 --- /dev/null +++ b/api/v1/solo-kit.json @@ -0,0 +1,17 @@ +{ + "name": "sqoop.solo.io", + "version": "v1", + "resource_groups": { + "api.sqoop.solo.io": [ + { + "name": "ResolverMap", + "package": "sqoop.solo.io" + }, + { + "name": "Schema", + "package": "sqoop.solo.io" + } + ] + }, + "docs_dir": "docs/v1" +} \ No newline at end of file diff --git a/ci/check-code-and-docs-gen.sh b/ci/check-code-and-docs-gen.sh new file mode 100755 index 0000000..373b803 --- /dev/null +++ b/ci/check-code-and-docs-gen.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +set -ex + +protoc --version + +if [ ! -f .gitignore ]; then + echo "_output" > .gitignore +fi + +git init +git add . +git commit -m "set up dummy repo for diffing" -q + +git clone https://github.com/solo-io/solo-kit /workspace/gopath/src/github.com/solo-io/solo-kit +git clone https://github.com/solo-io/gloo /workspace/gopath/src/github.com/solo-io/gloo + +make update-deps +make pin-repos + +PATH=/workspace/gopath/bin:$PATH + +set +e + +make generated-code -B > /dev/null +if [[ $? -ne 0 ]]; then + echo "Code generation failed" + exit 1; +fi +if [[ $(git status --porcelain | wc -l) -ne 0 ]]; then + echo "Generating code produced a non-empty diff." + echo "Try running 'dep ensure && make update-deps generated-code -B' then re-pushing." + git status --porcelain + git diff | cat + exit 1; +fi + +if [[ $(git --git-dir=/workspace/gopath/src/github.com/solo-io/gloo/.git --work-tree=/workspace/gopath/src/github.com/solo-io/gloo status --porcelain | wc -l) -ne 0 ]]; then +echo "Generating code produced a non-empty diff in the gloo repo" + echo "Make sure the go_import directory in protos is set to directory in sqoop, not in gloo." + git --git-dir=/workspace/gopath/src/github.com/solo-io/gloo/.git --work-tree=/workspace/gopath/src/github.com/solo-io/gloo status --porcelain + git --git-dir=/workspace/gopath/src/github.com/solo-io/gloo/.git --work-tree=/workspace/gopath/src/github.com/solo-io/gloo diff | cat + exit 1; +fi diff --git a/ci/push-docs.sh b/ci/push-docs.sh new file mode 100755 index 0000000..4bfa05f --- /dev/null +++ b/ci/push-docs.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash + +# Requires $tag shell variable and $GITHUB_TOKEN environment variable + +set -e +xargs=$(which gxargs || which xargs) + +# Validate settings. +[ "$TRACE" ] && set -x + +CONFIG=$@ + +for line in $CONFIG; do + eval "export ${line}" +done + +github_token_no_spaces=$(echo $GITHUB_TOKEN | tr -d '[:space:]') +branch="docs-gloo-$tag" + +set +x +echo "Cloning solo-docs repo" +git clone https://soloio-bot:$github_token_no_spaces@github.com/solo-io/solo-docs.git +[ "$TRACE" ] && set -x + +git config --global user.name "soloio-bot" +(cd solo-docs && git checkout -b $branch) + +# Sqoop +if [ -d "solo-docs/sqoop/docs/v1/github.com/solo-io/sqoop" ]; then + rm -r solo-docs/sqoop/docs/v1/github.com/solo-io/sqoop +fi +cp -r docs/v1/github.com/solo-io/sqoop solo-docs/sqoop/docs/v1/github.com/solo-io/gloo + +# Gloo +if [ -d "solo-docs/sqoop/docs/v1/github.com/solo-io/gloo" ]; then + rm -r solo-docs/sqoop/docs/v1/github.com/solo-io/gloo +fi +cp -r docs/v1/github.com/solo-io/gloo solo-docs/sqoop/docs/v1/github.com/solo-io/sqoop + +# Solo Kit +if [ -d "solo-docs/sqoop/docs/v1/github.com/solo-io/solo-kit" ]; then + rm -r solo-docs/sqoop/docs/v1/github.com/solo-io/solo-kit +fi +cp -r docs/v1/github.com/solo-io/solo-kit solo-docs/sqoop/docs/v1/github.com/solo-io/solo-kit + +# Gogoproto +if [ -d "solo-docs/sqoop/docs/v1/gogoproto" ]; then + rm -r solo-docs/sqoop/docs/v1/gogoproto +fi +cp -r docs/v1/gogoproto solo-docs/sqoop/docs/v1/gogoproto + +# Google +if [ -d "solo-docs/sqoop/docs/v1/google" ]; then + rm -r solo-docs/sqoop/docs/v1/google +fi +cp -r docs/v1/google solo-docs/sqoop/docs/v1/google + +(cd solo-docs && git add .) + +if [[ $( (cd solo-docs && git status --porcelain) | wc -l) -eq 0 ]]; then + echo "No changes to sqoop docs, exiting." + rm -rf solo-docs + exit 0; +fi + +(cd solo-docs && git commit -m "Add docs for tag $tag") +(cd solo-docs && git push --set-upstream origin $branch) + +curl -v -H "Authorization: token $github_token_no_spaces" -H "Content-Type:application/json" -X POST https://api.github.com/repos/solo-io/solo-docs/pulls -d \ +'{"title":"Update docs for gloo '"$tag"'", "body": "Update docs for gloo '"$tag"'", "head": "'"$branch"'", "base": "master"}' + +rm -rf solo-docs diff --git a/ci/spell.sh b/ci/spell.sh new file mode 100755 index 0000000..20a6fdd --- /dev/null +++ b/ci/spell.sh @@ -0,0 +1,93 @@ +#!/bin/bash + +# this file as adapted from here: https://github.com/envoyproxy/envoy/blob/master/tools/check_spelling.sh + +# Applies requisite code formatters to the source tree +# check_spelling.sh + +# Why choose misspell? +# https://github.com/client9/misspell#what-are-other-misspelling-correctors-and-whats-wrong-with-them + +set -u +set -e + +VERSION="0.3.4" +LINUX_MISSPELL_SHA="34d489dbc5ddb4dfd6d3cfac9fde8660e6c37e6c" +MAC_MISSPELL_SHA="f2607e2297b9e8af562e384c38045033375c7433" +TMP_DIR="/tmp" +OS="" + +MISSPELL_ARGS="-error -o stderr" + +if [[ "$#" -lt 1 ]]; then + echo "Usage: $0 check|fix" + exit -1 +fi + +if [[ "$1" == "fix" ]]; then + MISSPELL_ARGS="-w" +fi + +if [[ "$(uname)" == "Darwin" ]]; then + OS="mac" +elif [[ "$(uname)" == "Linux" ]]; then + OS="linux" +else + echo "Current only support mac/Linux" + exit 1 +fi + +SCRIPTPATH=$( cd "$(dirname "$0")" ; pwd -P ) +ROOTDIR="${SCRIPTPATH}/.." +cd "$ROOTDIR" + +BIN_FILENAME="misspell_"${VERSION}"_"${OS}"_64bit.tar.gz" +# Install tools we need +if [[ ! -e "${TMP_DIR}/misspell" ]]; then + if ! wget https://github.com/client9/misspell/releases/download/v"${VERSION}"/"${BIN_FILENAME}" \ + -O "${TMP_DIR}/${BIN_FILENAME}" --no-verbose --tries=3 -o "${TMP_DIR}/wget.log"; then + cat "${TMP_DIR}/wget.log" + exit -1 + fi + tar -xvf "${TMP_DIR}/${BIN_FILENAME}" -C "${TMP_DIR}" &> /dev/null +fi + +ACTUAL_SHA="" +EXPECT_SHA="" + +if [[ "${OS}" == "linux" ]]; then + ACTUAL_SHA=$(sha1sum "${TMP_DIR}"/misspell|cut -d' ' -f1) + EXPECT_SHA="${LINUX_MISSPELL_SHA}" +else + ACTUAL_SHA=$(shasum -a 1 "${TMP_DIR}"/misspell|cut -d' ' -f1) + EXPECT_SHA="${MAC_MISSPELL_SHA}" +fi + +if [[ ! ${ACTUAL_SHA} == ${EXPECT_SHA} ]]; then + echo "Expect shasum is ${ACTUAL_SHA}, but actual is shasum ${EXPECT_SHA}" + exit 1 +fi + +chmod +x "${TMP_DIR}/misspell" + +# Spell checking +# All the skipping files are defined in ci/spelling_skip_files.txt +SPELLING_SKIP_FILES="${ROOTDIR}/ci/spelling_skip_files.txt" + +# All the ignore words are defined in ci/spelling_whitelist_words.txt +SPELLING_WHITELIST_WORDS_FILE="${ROOTDIR}/ci/spelling_whitelist_words.txt" + +WHITELIST_WORDS=$(echo -n $(cat "${SPELLING_WHITELIST_WORDS_FILE}" | \ + grep -v "^#"|grep -v "^$") | tr ' ' ',') +SKIP_FILES=$(echo $(cat "${SPELLING_SKIP_FILES}") | sed "s| | -e |g") +if [ -z "$SKIP_FILES"]; then + FILTER_FILES="cat" +else + FILTER_FILES="grep -v -e \"${SKIP_FILES}\"" +fi + +LSCOMMAND=${LSCOMMAND:-"git ls-files"} +$LSCOMMAND | $FILTER_FILES | xargs "${TMP_DIR}/misspell" -i \ + "${WHITELIST_WORDS}" ${MISSPELL_ARGS} + +echo Done diff --git a/hack/upload-github-release-asset.sh b/ci/upload-github-release-asset.sh similarity index 79% rename from hack/upload-github-release-asset.sh rename to ci/upload-github-release-asset.sh index 9549944..e33ad14 100755 --- a/hack/upload-github-release-asset.sh +++ b/ci/upload-github-release-asset.sh @@ -15,9 +15,6 @@ # # Script to upload a release asset using the GitHub API v3. # -# Example: -# -# upload-github-release-asset.sh github_api_token=TOKEN owner=stefanbuck repo=playground tag=v0.1.1 filename=./build.zip # # Check dependencies. @@ -33,11 +30,13 @@ for line in $CONFIG; do eval "export ${line}" done +github_token_no_spaces=$(echo $GITHUB_TOKEN | tr -d '[:space:]') + # Define variables. GH_API="https://api.github.com" GH_REPO="$GH_API/repos/$owner/$repo" GH_TAGS="$GH_REPO/releases/tags/$tag" -AUTH="Authorization: token $github_api_token" +AUTH="Authorization: token $github_token_no_spaces" WGET_ARGS="--content-disposition --auth-no-challenge --no-cookie" CURL_ARGS="-LJO#" @@ -45,7 +44,6 @@ if [[ "$tag" == 'LATEST' ]]; then GH_TAGS="$GH_REPO/releases/latest" fi - # Validate token. curl -o /dev/null -sH "$AUTH" $GH_REPO || { echo "Error: Invalid repo, token or network issue!"; exit 1; } @@ -62,4 +60,7 @@ echo "Uploading asset... " # Construct url GH_ASSET="https://uploads.github.com/repos/$owner/$repo/releases/$id/assets?name=$(basename $filename)" -curl --data-binary @"$filename" -H "Authorization: token $github_api_token" -H "Content-Type: application/octet-stream" $GH_ASSET \ No newline at end of file +curl --data-binary @"$filename" -H "Authorization: token $github_token_no_spaces" -H "Content-Type: application/octet-stream" $GH_ASSET +if [[ "$sha" == 'TRUE' ]]; then + curl -d $(shasum -a 256 ${filename}) -H "Authorization: token $github_token_no_spaces" -H "Content-Type: application/octet-stream" $GH_ASSET.sha256 +fi diff --git a/cli/cmd/docs/main.go b/cli/cmd/docs/main.go new file mode 100644 index 0000000..b3c0c41 --- /dev/null +++ b/cli/cmd/docs/main.go @@ -0,0 +1,67 @@ +package main + +import ( + "bytes" + "log" + "path/filepath" + "strings" + "text/template" + + "github.com/solo-io/sqoop/cli/pkg/cmd" + "github.com/solo-io/sqoop/version" + "github.com/spf13/cobra" + "github.com/spf13/cobra/doc" +) + +const FrontMatter = `--- +title: "{{ replace .Name "_" " " }}" +weight: 5 +--- +` + +var funcMap = template.FuncMap{ + "title": strings.Title, + "replace": func(s, old, new string) string { return strings.Replace(s, old, new, -1) }, +} + +var FrontMatterTemplate = template.Must(template.New("frontmatter").Funcs(funcMap).Parse(FrontMatter)) + +func renderFrontMatter(filename string) string { + _, justFilename := filepath.Split(filename) + ext := filepath.Ext(justFilename) + justFilename = justFilename[:len(justFilename)-len(ext)] + info := struct { + Name string + }{ + Name: justFilename, + } + var buf bytes.Buffer + err := FrontMatterTemplate.Execute(&buf, info) + if err != nil { + panic(err) + } + return buf.String() +} + +func main() { + app := cmd.App(version.Version) + disableAutoGenTag(app) + //emptyStr := func(s string) string { return "" } + linkHandler := func(s string) string { + if strings.HasSuffix(s, ".md") { + return filepath.Join("..", s[:len(s)-3]) + } + return s + } + err := doc.GenMarkdownTreeCustom(app, "./docs/cli", renderFrontMatter, linkHandler) + if err != nil { + log.Fatal(err) + } +} + +func disableAutoGenTag(c *cobra.Command) { + c.DisableAutoGenTag = true + for _, c := range c.Commands() { + disableAutoGenTag(c) + } +} diff --git a/cli/cmd/main.go b/cli/cmd/main.go new file mode 100644 index 0000000..a0f3968 --- /dev/null +++ b/cli/cmd/main.go @@ -0,0 +1,24 @@ +package main + +import ( + "os" + "time" + + "github.com/solo-io/sqoop/version" + + check "github.com/solo-io/go-checkpoint" + "github.com/solo-io/sqoop/cli/pkg/cmd" +) + +func main() { + start := time.Now() + defer check.CallCheck("sqoopctl", version.Version, start) + + app := cmd.App(version.Version) + if err := app.Execute(); err != nil { + if err != nil { + panic(err) + } + os.Exit(0) + } +} diff --git a/cli/pkg/cmd/install/kube.go b/cli/pkg/cmd/install/kube.go new file mode 100644 index 0000000..8b3e6f5 --- /dev/null +++ b/cli/pkg/cmd/install/kube.go @@ -0,0 +1,30 @@ +package install + +import ( + "github.com/pkg/errors" + "github.com/solo-io/go-utils/cliutils" + "github.com/solo-io/sqoop/cli/pkg/flagutils" + _ "k8s.io/client-go/plugin/pkg/client/auth" + + "github.com/solo-io/sqoop/cli/pkg/options" + "github.com/spf13/cobra" +) + +func KubeCmd(opts *options.Options, optionsFunc... cliutils.OptionsFunc) *cobra.Command { + cmd := &cobra.Command{ + Use: "kube", + Short: "install sqoop on kubernetes", + Long: "requires kubectl to be installed", + RunE: func(cmd *cobra.Command, args []string) error { + if err := installFromUri(opts, opts.Install.ManifestOverride, sqoopTemplateUrl); err != nil { + return errors.Wrapf(err, "installing sqoop from manifest") + } + return nil + }, + } + pflags := cmd.PersistentFlags() + flagutils.AddInstallFlags(pflags, &opts.Install) + + cliutils.ApplyOptions(cmd, optionsFunc) + return cmd +} diff --git a/cli/pkg/cmd/install/root.go b/cli/pkg/cmd/install/root.go new file mode 100644 index 0000000..fc5059d --- /dev/null +++ b/cli/pkg/cmd/install/root.go @@ -0,0 +1,40 @@ +package install + +import ( + "github.com/solo-io/gloo/projects/gloo/cli/pkg/constants" + "github.com/solo-io/go-utils/cliutils" + "github.com/solo-io/go-utils/errors" + "github.com/solo-io/sqoop/cli/pkg/options" + "github.com/spf13/cobra" +) + +func InstallCmd(opts *options.Options, optionsFunc ...cliutils.OptionsFunc) *cobra.Command { + cmd := &cobra.Command{ + Use: constants.INSTALL_COMMAND.Use, + Short: constants.INSTALL_COMMAND.Short, + Long: constants.INSTALL_COMMAND.Long, + } + + cmd.AddCommand( + KubeCmd(opts), + ) + + cliutils.ApplyOptions(cmd, optionsFunc) + return cmd +} + +func UninstallCmd(opts *options.Options, optionsFunc ...cliutils.OptionsFunc) *cobra.Command { + cmd := &cobra.Command{ + Use: constants.UNINSTALL_COMMAND.Use, + Short: constants.UNINSTALL_COMMAND.Short, + Long: constants.UNINSTALL_COMMAND.Long, + RunE: func(cmd *cobra.Command, args []string) error { + if err := deleteNamespace(installNamespace); err != nil { + return errors.Wrapf(err, "delete sqoop failed") + } + return nil + }, + } + cliutils.ApplyOptions(cmd, optionsFunc) + return cmd +} diff --git a/cli/pkg/cmd/install/util.go b/cli/pkg/cmd/install/util.go new file mode 100644 index 0000000..6fe65e3 --- /dev/null +++ b/cli/pkg/cmd/install/util.go @@ -0,0 +1,192 @@ +package install + +import ( + "bytes" + "context" + "fmt" + "github.com/pkg/errors" + "github.com/solo-io/gloo/pkg/cliutil" + "github.com/solo-io/gloo/pkg/cliutil/install" + "github.com/solo-io/gloo/projects/gloo/pkg/api/v1" + "github.com/solo-io/go-utils/kubeutils" + "github.com/solo-io/solo-kit/pkg/api/v1/clients/factory" + "github.com/solo-io/solo-kit/pkg/api/v1/clients/kube" + "github.com/solo-io/sqoop/cli/pkg/options" + "github.com/solo-io/sqoop/pkg/defaults" + "github.com/solo-io/sqoop/version" + "io" + "io/ioutil" + kubev1 "k8s.io/api/core/v1" + kubeerrs "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/helm/pkg/chartutil" + "k8s.io/helm/pkg/renderutil" + "os" + "os/exec" + "path" +) + +const ( + installNamespace = defaults.GlooSystem + + sqoopTemplateUrl = "https://storage.googleapis.com/sqoop-helm/charts/sqoop-%s.tgz" +) + +func preInstall(namespace string) error { + if err := registerSettingsCrd(); err != nil { + return errors.Wrapf(err, "registering settings crd") + } + if err := createNamespaceIfNotExist(namespace); err != nil { + return errors.Wrapf(err, "creating namespace") + } + return nil +} + +func getFinalUri(overrideUri string, opts *options.Options) (string, error) { + var uri string + switch { + case overrideUri != "": + uri = overrideUri + case !version.IsReleaseVersion(): + if opts.Install.ReleaseVersion == "" { + return "", errors.Errorf("you must provide a file or a release version containing the manifest when running an unreleased version of glooctl.") + } + uri = fmt.Sprintf(sqoopTemplateUrl, opts.Install.ReleaseVersion) + default: + uri = fmt.Sprintf(sqoopTemplateUrl, version.Version) + + } + + return uri, nil +} + +func installFromUri(opts *options.Options, manifestUri, valuesFileName string) error { + // Pre-install step writes to k8s. Run only if this is not a dry run. + if !opts.Install.DryRun { + if err := preInstall(opts.Install.Namespace); err != nil { + return errors.Wrapf(err, "pre-install failed") + } + } + + uri, err := getFinalUri(manifestUri, opts) + if err != nil { + return err + } + + var manifestBytes []byte + switch path.Ext(uri) { + case ".json", ".yaml", ".yml": + var err error + manifestBytes, err = getFileManifestBytes(uri) + if err != nil { + return err + } + case ".tgz": + var err error + renderOpts := renderutil.Options{ + ReleaseOptions: chartutil.ReleaseOptions{ + Namespace: opts.Install.Namespace, + Name: "sqoop", + }, + } + + manifestBytes, err = install.GetHelmManifest(uri, valuesFileName, renderOpts, install.ExcludeEmptyManifests) + if err != nil { + return err + } + default: + return errors.Errorf("unsupported file extension in manifest URI: %s", path.Ext(uri)) + } + + return installManifest(manifestBytes, opts) +} + +func installManifest(manifest []byte, opts *options.Options) error { + if opts.Install.DryRun { + fmt.Printf("%s", manifest) + return nil + } + if err := kubectlApply(manifest, opts.Install.Namespace); err != nil { + return errors.Wrapf(err, "running kubectl apply on manifest") + } + return nil +} + +func kubectlApply(manifest []byte, namespace string) error { + return kubectl(bytes.NewBuffer(manifest), "apply", "-n", namespace, "-f", "-") +} + +func kubectl(stdin io.Reader, args ...string) error { + kubectl := exec.Command("kubectl", args...) + if stdin != nil { + kubectl.Stdin = stdin + } + kubectl.Stdout = os.Stdout + kubectl.Stderr = os.Stderr + return kubectl.Run() +} + +func registerSettingsCrd() error { + cfg, err := kubeutils.GetConfig("", os.Getenv("KUBECONFIG")) + if err != nil { + return err + } + + settingsClient, err := v1.NewSettingsClient(&factory.KubeResourceClientFactory{ + Crd: v1.SettingsCrd, + Cfg: cfg, + SharedCache: kube.NewKubeCache(context.TODO()), + }) + + return settingsClient.Register() +} + +func createNamespaceIfNotExist(namespace string) error { + restCfg, err := kubeutils.GetConfig("", "") + if err != nil { + return err + } + kubeClient, err := kubernetes.NewForConfig(restCfg) + if err != nil { + return err + } + installNamespace := &kubev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: namespace, + }, + } + if _, err := kubeClient.CoreV1().Namespaces().Create(installNamespace); err != nil && !kubeerrs.IsAlreadyExists(err) { + return err + } + return nil +} + +func deleteNamespace(namespace string) error { + restCfg, err := kubeutils.GetConfig("", "") + if err != nil { + return err + } + kubeClient, err := kubernetes.NewForConfig(restCfg) + if err != nil { + return err + } + if err := kubeClient.CoreV1().Namespaces().Delete(namespace, nil); err != nil { + return err + } + return nil +} + +func getFileManifestBytes(uri string) ([]byte, error) { + manifestFile, err := cliutil.GetResource(uri) + if err != nil { + return nil, errors.Wrapf(err, "getting manifest file %v", uri) + } + //noinspection GoUnhandledErrorResult + defer manifestFile.Close() + manifestBytes, err := ioutil.ReadAll(manifestFile) + if err != nil { + return nil, errors.Wrapf(err, "reading manifest file %v", uri) + } + return manifestBytes, nil +} diff --git a/cli/pkg/cmd/resolvermap/resolvermap_delete.go b/cli/pkg/cmd/resolvermap/resolvermap_delete.go new file mode 100644 index 0000000..692078a --- /dev/null +++ b/cli/pkg/cmd/resolvermap/resolvermap_delete.go @@ -0,0 +1,42 @@ +package resolvermap + +import ( +"fmt" +"github.com/solo-io/go-utils/cliutils" +"github.com/solo-io/solo-kit/pkg/api/v1/clients" +"github.com/solo-io/sqoop/cli/pkg/common" +"github.com/solo-io/sqoop/cli/pkg/helpers" +"github.com/solo-io/sqoop/cli/pkg/options" +"github.com/spf13/cobra" +) + +func Reset(opts *options.Options, optionsFunc... cliutils.OptionsFunc) *cobra.Command { + cmd := &cobra.Command{ + Use: "reset [NAME]", + Short: "reset a resolver map by its name", + RunE: func(cmd *cobra.Command, args []string) error { + err := common.SetResourceName(&opts.Metadata, args) + if err != nil { + return err + } + if err := resetResolverMap(opts); err != nil { + return err + } + fmt.Println("resolvermap reset successfully") + return nil + }, + Args: common.RequiredNameArg, + } + + + cliutils.ApplyOptions(cmd, optionsFunc) + return cmd +} + +func resetResolverMap(opts *options.Options) error { + client, err := helpers.ResolverMapClient() + if err != nil { + return err + } + return client.Delete(opts.Metadata.Namespace, opts.Metadata.Name, clients.DeleteOpts{}) +} \ No newline at end of file diff --git a/cli/pkg/cmd/resolvermap/resolvermap_register.go b/cli/pkg/cmd/resolvermap/resolvermap_register.go new file mode 100644 index 0000000..1b36d11 --- /dev/null +++ b/cli/pkg/cmd/resolvermap/resolvermap_register.go @@ -0,0 +1,124 @@ +package resolvermap + +import ( + "fmt" + glooV1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1" + "github.com/solo-io/gloo/projects/gloo/pkg/api/v1/plugins/rest" + "github.com/solo-io/go-utils/cliutils" + "github.com/solo-io/solo-kit/pkg/api/v1/clients" + "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" + "github.com/solo-io/sqoop/cli/pkg/common" + "github.com/solo-io/sqoop/cli/pkg/flagutils" + "github.com/solo-io/sqoop/cli/pkg/helpers" + "github.com/solo-io/sqoop/cli/pkg/options" + "github.com/solo-io/sqoop/pkg/api/v1" + "github.com/spf13/cobra" +) + +func Register(opts *options.Options, optionsFunc ...cliutils.OptionsFunc) *cobra.Command { + cmd := &cobra.Command{ + Use: "register TypeName FieldName -f resolver.yaml [-s schema-name]", + Short: "Register a resolver for a field in your Schema", + Long: `Sets the resolver for a field in your schema. TypeName.FieldName will always be resolved using this resolver +Resolvers must be defined in yaml format. +See the documentation at https://sqoop.solo.io/v1/resolver_map/#sqoop.api.v1.Resolver for the API specification for Sqoop Resolvers`, + RunE: func(cmd *cobra.Command, args []string) error { + if err := common.SetResourceName(&opts.Metadata, args); err != nil { + return err + } + if err := ensureResolverMapParams(&opts.ResolverMap, args); err != nil { + return err + } + if err := registerResolver(opts); err != nil { + return err + } + fmt.Println("resolver map registered successfully") + return nil + }, + Args: ensureResolverMapArgs, + } + + flagutils.AddResolverMapFlags(cmd.PersistentFlags(), &opts.ResolverMap) + + cliutils.ApplyOptions(cmd, optionsFunc) + return cmd +} + +func registerResolver(opts *options.Options) error { + client, err := helpers.ResolverMapClient() + if err != nil { + return err + } + + var responseTemplate *v1.ResponseTemplate + if opts.ResolverMap.ResponseTemplate != "" { + responseTemplate = &v1.ResponseTemplate{ + Body: opts.ResolverMap.ResponseTemplate, + } + } + var requestTemplate *v1.RequestTemplate + if opts.ResolverMap.RequestTemplate != "" { + requestTemplate = &v1.RequestTemplate{ + Body: opts.ResolverMap.RequestTemplate, + } + } + + + resolver := &v1.FieldResolver{ + Resolver: &v1.FieldResolver_GlooResolver{ + GlooResolver: &v1.GlooResolver{ + ResponseTemplate: responseTemplate, + RequestTemplate: requestTemplate, + Action: &glooV1.RouteAction{ + Destination: &glooV1.RouteAction_Single{ + Single: &glooV1.Destination{ + Upstream: core.ResourceRef{ + Name: opts.ResolverMap.Upstream, + Namespace: opts.Metadata.Namespace, + }, + DestinationSpec: &glooV1.DestinationSpec{ + DestinationType: &glooV1.DestinationSpec_Rest{ + Rest: &rest.DestinationSpec{ + FunctionName: opts.ResolverMap.Function, + }, + }, + }, + }, + }, + }, + }, + }, + } + + existingResolverMap, err := resolverMapForSchema(opts, client) + if err != nil { + return err + } + existingResolverMap.Types[opts.ResolverMap.TypeName].Fields[opts.ResolverMap.FieldName] = resolver + _, err = client.Write(existingResolverMap, clients.WriteOpts{OverwriteExisting:true}) + if err != nil { + return err + } + return nil +} + +func resolverMapForSchema(opts *options.Options, client v1.ResolverMapClient) (*v1.ResolverMap, error) { + resolverMaps, err := client.List(opts.Metadata.Namespace, clients.ListOpts{}) + if err != nil { + return nil, err + } + + for _, rm := range resolverMaps { + typResolver, ok := rm.Types[opts.ResolverMap.TypeName] + if !ok { + continue + } + if _, ok := typResolver.Fields[opts.ResolverMap.FieldName]; !ok { + continue + } + return rm, nil + } + return nil, fmt.Errorf("cannot find a resolver map for type %v with field %v", + opts.ResolverMap.TypeName, + opts.ResolverMap.FieldName) +} diff --git a/pkg/operator/gloo_suite_test.go b/cli/pkg/cmd/resolvermap/resolvermap_suite_test.go similarity index 53% rename from pkg/operator/gloo_suite_test.go rename to cli/pkg/cmd/resolvermap/resolvermap_suite_test.go index 57bb0f9..2872f42 100644 --- a/pkg/operator/gloo_suite_test.go +++ b/cli/pkg/cmd/resolvermap/resolvermap_suite_test.go @@ -1,4 +1,4 @@ -package operator_test +package resolvermap_test import ( "testing" @@ -7,7 +7,7 @@ import ( . "github.com/onsi/gomega" ) -func TestGloo(t *testing.T) { +func TestResolvermap(t *testing.T) { RegisterFailHandler(Fail) - RunSpecs(t, "Gloo Suite") + RunSpecs(t, "ResolverMap Suite") } diff --git a/cli/pkg/cmd/resolvermap/resolvermap_test.go b/cli/pkg/cmd/resolvermap/resolvermap_test.go new file mode 100644 index 0000000..0f688c2 --- /dev/null +++ b/cli/pkg/cmd/resolvermap/resolvermap_test.go @@ -0,0 +1,195 @@ +package resolvermap_test + +import ( + "fmt" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + glooHelpers "github.com/solo-io/gloo/projects/gloo/cli/pkg/helpers" + glooV1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1" + "github.com/solo-io/sqoop/cli/pkg/helpers" + "github.com/solo-io/sqoop/pkg/api/v1" + "github.com/solo-io/sqoop/pkg/defaults" + "sigs.k8s.io/yaml" + + "github.com/solo-io/solo-kit/pkg/api/v1/clients" + "github.com/solo-io/sqoop/cli/pkg/testutils" +) + +var _ = Describe("Schema", func() { + + registerResolverMap := func() { + client := helpers.MustResolverMapClient() + var rm v1.ResolverMap + err := yaml.Unmarshal([]byte(resolverMap), &rm) + Expect(err).NotTo(HaveOccurred()) + rm.Types = map[string]*v1.TypeResolver{ + "Query": &v1.TypeResolver{ + Fields: map[string]*v1.FieldResolver{ + "pet": nil, + "pets": nil, + }, + }, + "Mutation": &v1.TypeResolver{ + Fields: map[string]*v1.FieldResolver{ + "addPet": nil, + }, + }, + "Pet": &v1.TypeResolver{ + Fields: map[string]*v1.FieldResolver{ + "id": nil, + "name": nil, + }, + }, + } + _, err = client.Write(&rm, clients.WriteOpts{}) + Expect(err).NotTo(HaveOccurred()) + } + + registerUpstream := func() { + client := glooHelpers.MustUpstreamClient() + var us glooV1.Upstream + err := yaml.Unmarshal([]byte(petstoreUpstream), &us) + Expect(err).NotTo(HaveOccurred()) + _, err = client.Write(&us, clients.WriteOpts{}) + Expect(err).NotTo(HaveOccurred()) + } + + BeforeEach(func() { + // create proper resolver map because event loop is not running + helpers.UseMemoryClients() + glooHelpers.UseMemoryClients() + registerResolverMap() + }) + + getResolverMap := func(name string) *v1.ResolverMap { + rm, err := helpers.MustResolverMapClient().Read(defaults.GlooSystem, name, clients.ReadOpts{}) + Expect(err).NotTo(HaveOccurred()) + return rm + } + + Context("register", func() { + It("should register properly", func() { + registerUpstream() + err := testutils.Sqoopctl(fmt.Sprintf("resolvermap register -u %s -g %s -s %s Query pets", upstreamName, upstreamFunctionName, schemaName)) + Expect(err).NotTo(HaveOccurred()) + getResolverMap(schemaName) + }) + + It("should error when schema not provided", func() { + err := testutils.Sqoopctl(fmt.Sprintf("resolvermap register -u %s -g %s Query pets", upstreamName, upstreamFunctionName)) + Expect(err).To(HaveOccurred()) + }) + + It("should error when upstream not provided", func() { + err := testutils.Sqoopctl(fmt.Sprintf("resolvermap register -g %s -s %s Query pets", upstreamFunctionName, schemaName)) + Expect(err).To(HaveOccurred()) + }) + + It("should error when function not provided", func() { + err := testutils.Sqoopctl(fmt.Sprintf("resolvermap register -u %s -s %s Query pets", upstreamName, schemaName)) + Expect(err).To(HaveOccurred()) + }) + + It("should error when # of args incorrect", func() { + err := testutils.Sqoopctl(fmt.Sprintf("resolvermap register -u %s -g %s -s %s Query", upstreamName, upstreamFunctionName, schemaName)) + Expect(err).To(HaveOccurred()) + }) + + It("should error when type and field don't match resolver map", func() { + err := testutils.Sqoopctl(fmt.Sprintf("resolvermap register -u %s -g %s -s %s Query test", upstreamName, upstreamFunctionName, schemaName)) + Expect(err).To(HaveOccurred()) + }) + }) + +}) + +const schemaName = "one" +const resolverMap = ` +apiVersion: sqoop.solo.io/v1 +kind: ResolverMap +metadata: + annotations: + created_for: one + name: one + namespace: gloo-system +spec: + types: + Mutation: + fields: + addPet: {} + Pet: + fields: + id: {} + name: {} + Query: + fields: + pet: {} + pets: {} +` +const upstreamName = "gloo-system-petstore-8080" +const upstreamFunctionName = "findPets" +const petstoreUpstream = ` +apiVersion: gloo.solo.io/v1 +kind: Upstream +metadata: + labels: + service: petstore + name: gloo-system-petstore-8080 + namespace: gloo-system +spec: + discoveryMetadata: {} + upstreamSpec: + kube: + selector: + app: petstore + serviceName: petstore + serviceNamespace: gloo-system + servicePort: 8080 + serviceSpec: + rest: + swaggerInfo: + url: http://petstore.gloo-system.svc.cluster.local:8080/swagger.json + transformations: + addPet: + body: + text: '{"id": {{ default(id, "") }},"name": "{{ default(name, "")}}","tag": + "{{ default(tag, "")}}"}' + headers: + :method: + text: POST + :path: + text: /api/pets + content-type: + text: application/json + deletePet: + headers: + :method: + text: DELETE + :path: + text: /api/pets/{{ default(id, "") }} + content-type: + text: application/json + findPetById: + body: {} + headers: + :method: + text: GET + :path: + text: /api/pets/{{ default(id, "") }} + content-length: + text: "0" + content-type: {} + transfer-encoding: {} + findPets: + body: {} + headers: + :method: + text: GET + :path: + text: /api/pets?tags={{default(tags, "")}}&limit={{default(limit, + "")}} + content-length: + text: "0" + content-type: {} + transfer-encoding: {} +` diff --git a/cli/pkg/cmd/resolvermap/root.go b/cli/pkg/cmd/resolvermap/root.go new file mode 100644 index 0000000..46cbe07 --- /dev/null +++ b/cli/pkg/cmd/resolvermap/root.go @@ -0,0 +1,23 @@ +package resolvermap + +import ( + "github.com/solo-io/go-utils/cliutils" + "github.com/solo-io/sqoop/cli/pkg/constants" + "github.com/solo-io/sqoop/cli/pkg/options" + "github.com/spf13/cobra" +) + +func ResolverMapCmd(opts *options.Options, optionsFunc ...cliutils.OptionsFunc) *cobra.Command { + cmd := &cobra.Command{ + Use: constants.RESOLVER_MAP_COMMAND.Use, + Short: constants.RESOLVER_MAP_COMMAND.Short, + } + + cmd.AddCommand( + Reset(opts), + Register(opts), + ) + + cliutils.ApplyOptions(cmd, optionsFunc) + return cmd +} diff --git a/cli/pkg/cmd/resolvermap/util.go b/cli/pkg/cmd/resolvermap/util.go new file mode 100644 index 0000000..9611021 --- /dev/null +++ b/cli/pkg/cmd/resolvermap/util.go @@ -0,0 +1,31 @@ +package resolvermap + +import ( + "fmt" + "github.com/solo-io/sqoop/cli/pkg/options" + "github.com/spf13/cobra" +) + +func ensureResolverMapParams(opts *options.ResolverMap, args []string) error { + if opts.SchemaName == "" { + return fmt.Errorf("schema name cannot be empty") + } + if opts.Function == "" { + return fmt.Errorf("function name cannot be empty") + } + if opts.Upstream == "" { + return fmt.Errorf("upstream cannot be empty") + } + opts.TypeName = args[0] + opts.FieldName = args[1] + return nil +} + +func ensureResolverMapArgs(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + return fmt.Errorf("2 args are required (typeName, fieldName) %d were found", len(args)) + } + return nil +} + + diff --git a/cli/pkg/cmd/root.go b/cli/pkg/cmd/root.go new file mode 100644 index 0000000..bc61aa0 --- /dev/null +++ b/cli/pkg/cmd/root.go @@ -0,0 +1,47 @@ +package cmd + +import ( + "github.com/solo-io/go-utils/cliutils" + "github.com/solo-io/sqoop/cli/pkg/cmd/install" + "github.com/solo-io/sqoop/cli/pkg/cmd/resolvermap" + "github.com/solo-io/sqoop/cli/pkg/cmd/schema" + "github.com/solo-io/sqoop/cli/pkg/flagutils" + "github.com/solo-io/sqoop/cli/pkg/options" + "github.com/spf13/cobra" +) + +func App(version string, optionsFunc ...cliutils.OptionsFunc) *cobra.Command { + + opts := &options.Options{} + + app := &cobra.Command{ + Use: "sqoopctl", + Short: "Interact with Sqoop's storage API from the command line", + Long: "As Sqoop features a storage-based API, direct communication with " + + "the Sqoop server is not necessary. sqoopctl simplifies the administration of " + + "Sqoop by providing an easy way to create, read, update, and delete Sqoop storage objects.\n\n" + + "" + + "The primary concerns of sqoopctl are Schemas and ResolverMaps. Schemas contain your GraphQL schema;" + + " ResolverMaps define how your schema fields are resolved.\n\n" + + "" + + "Start by creating a schema using sqoopctl schema create --from-file ", + Run: func(cmd *cobra.Command, args []string) { + panic("not implemented") + }, + Version: version, + } + pflags := app.PersistentFlags() + flagutils.AddCommonFlags(pflags, &opts.Top) + + app.AddCommand( + install.InstallCmd(opts), + install.UninstallCmd(opts), + resolvermap.ResolverMapCmd(opts), + schema.SchemaCmd(opts), + ) + + // Complete additional passed in setup + cliutils.ApplyOptions(app, optionsFunc) + + return app +} diff --git a/cli/pkg/cmd/schema/root.go b/cli/pkg/cmd/schema/root.go new file mode 100644 index 0000000..29f03c4 --- /dev/null +++ b/cli/pkg/cmd/schema/root.go @@ -0,0 +1,27 @@ +package schema + +import ( + "github.com/solo-io/go-utils/cliutils" + "github.com/solo-io/sqoop/cli/pkg/constants" + "github.com/solo-io/sqoop/cli/pkg/flagutils" + "github.com/solo-io/sqoop/cli/pkg/options" + "github.com/spf13/cobra" +) + +func SchemaCmd(opts *options.Options, optionsFunc ...cliutils.OptionsFunc) *cobra.Command { + cmd := &cobra.Command{ + Use: constants.SCHEMA_COMMAND.Use, + Short: constants.SCHEMA_COMMAND.Short, + } + + cmd.AddCommand( + Create(opts), + Delete(opts), + Update(opts), + ) + + flagutils.AddMetadataFlags(cmd.PersistentFlags(), &opts.Metadata) + + cliutils.ApplyOptions(cmd, optionsFunc) + return cmd +} diff --git a/cli/pkg/cmd/schema/schema_create.go b/cli/pkg/cmd/schema/schema_create.go new file mode 100644 index 0000000..7209e4e --- /dev/null +++ b/cli/pkg/cmd/schema/schema_create.go @@ -0,0 +1,56 @@ +package schema + +import ( + "fmt" + "github.com/solo-io/go-utils/cliutils" + "github.com/solo-io/solo-kit/pkg/api/v1/clients" + "github.com/solo-io/sqoop/cli/pkg/common" + "github.com/solo-io/sqoop/cli/pkg/helpers" + "github.com/solo-io/sqoop/cli/pkg/options" + "github.com/solo-io/sqoop/pkg/api/v1" + "github.com/spf13/cobra" + "io/ioutil" +) + +func Create(opts *options.Options, optionsFunc ...cliutils.OptionsFunc) *cobra.Command { + cmd := &cobra.Command{ + Use: "create NAME -f ", + Short: "upload a schema to Sqoop from a local GraphQL Schema file", + RunE: func(cmd *cobra.Command, args []string) error { + err := common.SetResourceName(&opts.Metadata, args) + if err != nil { + return err + } + if err := createSchema(opts); err != nil { + return err + } + fmt.Println("schema created successfully") + return nil + }, + Args: common.RequiredNameArg, + } + + cliutils.ApplyOptions(cmd, optionsFunc) + return cmd +} + +func createSchema(opts *options.Options) error { + if opts.Top.File == "" { + return fmt.Errorf("schema file must be set") + } + client, err := helpers.SchemaClient() + if err != nil { + return err + } + inlineSchemaBytes, err := ioutil.ReadFile(opts.Top.File) + if err != nil { + return err + } + schema := &v1.Schema{ + Metadata: opts.Metadata, + InlineSchema: string(inlineSchemaBytes), + } + + _, err = client.Write(schema, clients.WriteOpts{}) + return err +} diff --git a/cli/pkg/cmd/schema/schema_delete.go b/cli/pkg/cmd/schema/schema_delete.go new file mode 100644 index 0000000..4f5a705 --- /dev/null +++ b/cli/pkg/cmd/schema/schema_delete.go @@ -0,0 +1,42 @@ +package schema + +import ( + "fmt" + "github.com/solo-io/go-utils/cliutils" + "github.com/solo-io/solo-kit/pkg/api/v1/clients" + "github.com/solo-io/sqoop/cli/pkg/common" + "github.com/solo-io/sqoop/cli/pkg/helpers" + "github.com/solo-io/sqoop/cli/pkg/options" + "github.com/spf13/cobra" +) + +func Delete(opts *options.Options, optionsFunc... cliutils.OptionsFunc) *cobra.Command { + cmd := &cobra.Command{ + Use: "delete [NAME]", + Short: "delete a schema by its name", + RunE: func(cmd *cobra.Command, args []string) error { + err := common.SetResourceName(&opts.Metadata, args) + if err != nil { + return err + } + if err := deleteSchema(opts); err != nil { + return err + } + fmt.Println("schema deleted successfully") + return nil + }, + Args: common.RequiredNameArg, + } + + + cliutils.ApplyOptions(cmd, optionsFunc) + return cmd +} + +func deleteSchema(opts *options.Options) error { + client, err := helpers.SchemaClient() + if err != nil { + return err + } + return client.Delete(opts.Metadata.Namespace, opts.Metadata.Name, clients.DeleteOpts{}) +} \ No newline at end of file diff --git a/pkg/exec/exec_suite_test.go b/cli/pkg/cmd/schema/schema_suite_test.go similarity index 57% rename from pkg/exec/exec_suite_test.go rename to cli/pkg/cmd/schema/schema_suite_test.go index 14296b7..d2b5531 100644 --- a/pkg/exec/exec_suite_test.go +++ b/cli/pkg/cmd/schema/schema_suite_test.go @@ -1,4 +1,4 @@ -package exec_test +package schema_test import ( "testing" @@ -7,7 +7,7 @@ import ( . "github.com/onsi/gomega" ) -func TestExec(t *testing.T) { +func TestSchema(t *testing.T) { RegisterFailHandler(Fail) - RunSpecs(t, "Exec Suite") + RunSpecs(t, "Schema Suite") } diff --git a/cli/pkg/cmd/schema/schema_update.go b/cli/pkg/cmd/schema/schema_update.go new file mode 100644 index 0000000..1dec765 --- /dev/null +++ b/cli/pkg/cmd/schema/schema_update.go @@ -0,0 +1,60 @@ +package schema + +import ( + "fmt" + "github.com/solo-io/go-utils/cliutils" + "github.com/solo-io/solo-kit/pkg/api/v1/clients" + "github.com/solo-io/sqoop/cli/pkg/common" + "github.com/solo-io/sqoop/cli/pkg/helpers" + "github.com/solo-io/sqoop/cli/pkg/options" + "github.com/solo-io/sqoop/pkg/api/v1" + "github.com/spf13/cobra" + "io/ioutil" +) + +func Update(opts *options.Options, optionsFunc... cliutils.OptionsFunc) *cobra.Command { + cmd := &cobra.Command{ + Use: "update NAME -f ", + Short: "upload a schema to Sqoop from a local GraphQL Schema file", + RunE: func(cmd *cobra.Command, args []string) error { + err := common.SetResourceName(&opts.Metadata, args) + if err != nil { + return err + } + if err := updateSchema(opts); err != nil { + return err + } + fmt.Println("schema updated successfully") + return nil + }, + } + + + cliutils.ApplyOptions(cmd, optionsFunc) + return cmd +} + +func updateSchema(opts *options.Options) error { + if opts.Top.File == "" { + return fmt.Errorf("schema file must be set") + } + client, err := helpers.SchemaClient() + if err != nil { + return err + } + existing, err := client.Read(opts.Metadata.Namespace, opts.Metadata.Name, clients.ReadOpts{}) + if err != nil { + return err + } + inlineSchemaBytes, err := ioutil.ReadFile(opts.Top.File) + if err != nil { + return err + } + schema := &v1.Schema{ + Metadata: existing.Metadata, + InlineSchema: string(inlineSchemaBytes), + } + + _, err = client.Write(schema, clients.WriteOpts{OverwriteExisting: true}) + return err +} \ No newline at end of file diff --git a/cli/pkg/cmd/schema/shema_test.go b/cli/pkg/cmd/schema/shema_test.go new file mode 100644 index 0000000..5f80724 --- /dev/null +++ b/cli/pkg/cmd/schema/shema_test.go @@ -0,0 +1,130 @@ +package schema_test + +import ( + "fmt" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/solo-io/sqoop/cli/pkg/helpers" + "github.com/solo-io/sqoop/pkg/api/v1" + "github.com/solo-io/sqoop/pkg/defaults" + "os" + + "github.com/solo-io/solo-kit/pkg/api/v1/clients" + "github.com/solo-io/sqoop/cli/pkg/testutils" +) + +const ( + schemaName = "one" + + update = "update" + create = "create" +) + +var _ = Describe("Schema", func() { + + BeforeEach(func() { + helpers.UseMemoryClients() + }) + + getSchema := func(name string) *v1.Schema { + schema, err := helpers.MustSchemaClient().Read(defaults.GlooSystem, name, clients.ReadOpts{}) + Expect(err).NotTo(HaveOccurred()) + return schema + } + + baseCommand := func(cmdType string) { + schemaFile := testutils.MustWriteTestFile(exampleSchema) + defer os.Remove(schemaFile) + err := testutils.Sqoopctl(fmt.Sprintf("schema %s %s -f %s", cmdType, schemaName, schemaFile)) + Expect(err).NotTo(HaveOccurred()) + schemaOne := getSchema(schemaName) + Expect(schemaOne.InlineSchema).To(Equal(exampleSchema)) + Expect(schemaOne.Metadata.Name).To(Equal(schemaName)) + } + + Context("create", func() { + It("can create a schema when a file is properly supplied", func() { + baseCommand("create") + }) + + It("fails when no schema is provided", func() { + err := testutils.Sqoopctl(fmt.Sprintf("schema create %s", schemaName)) + Expect(err).To(HaveOccurred()) + }) + + It("fails when no name is provided", func() { + schemaFile := testutils.MustWriteTestFile(exampleSchema) + defer os.Remove(schemaFile) + err := testutils.Sqoopctl(fmt.Sprintf("schema create -f %s", schemaFile)) + Expect(err).To(HaveOccurred()) + }) + }) + + Context("update", func() { + It("can update a schema when a file is properly supplied, and schema exists", func() { + baseCommand(create) + baseCommand(update) + }) + + It("fails when schema doesn't exist is provided", func() { + err := testutils.Sqoopctl(fmt.Sprintf("schema update %s -f %s", "incorrect_name", "hello")) + Expect(err).To(HaveOccurred()) + }) + + It("fails when no schema is provided", func() { + err := testutils.Sqoopctl(fmt.Sprintf("schema update %s", schemaName)) + Expect(err).To(HaveOccurred()) + }) + + It("fails when no name is provided", func() { + schemaFile := testutils.MustWriteTestFile(exampleSchema) + defer os.Remove(schemaFile) + err := testutils.Sqoopctl(fmt.Sprintf("schema update -f %s", schemaFile)) + Expect(err).To(HaveOccurred()) + }) + }) + + Context("delete", func() { + It("can delete a schema when schema exists", func() { + baseCommand(create) + err := testutils.Sqoopctl(fmt.Sprintf("schema delete %s", schemaName)) + Expect(err).NotTo(HaveOccurred()) + }) + + It("fails when schema doesn't exist is provided", func() { + err := testutils.Sqoopctl(fmt.Sprintf("schema delete %s", schemaName)) + Expect(err).To(HaveOccurred()) + }) + + It("fails when no name is provided", func() { + err := testutils.Sqoopctl(fmt.Sprintf("schema delete")) + Expect(err).To(HaveOccurred()) + }) + }) + +}) + +const exampleSchema = ` +# The query type, represents all of the entry points into our object graph +type Query { + pets: [Pet] + pet(id: Int!): Pet +} + +type Mutation { + addPet(pet: InputPet!): Pet +} + +type Pet{ + id: ID! + name: String! +} + +input InputPet{ + id: ID! + name: String! + tag: String +} +` + + diff --git a/cli/pkg/common/main.go b/cli/pkg/common/main.go new file mode 100644 index 0000000..dfe88e7 --- /dev/null +++ b/cli/pkg/common/main.go @@ -0,0 +1,25 @@ +package common + +import ( + "fmt" + "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" + "github.com/spf13/cobra" +) + +func RequiredNameArg(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return fmt.Errorf("name are required, no arg(s) found") + } + return nil +} + +func SetResourceName(opts *core.Metadata, args []string) error { + if len(args) > 0 { + opts.Name = args[0] + return nil + } + if opts.Name == "" { + return fmt.Errorf("no resource name could be found") + } + return nil +} \ No newline at end of file diff --git a/cli/pkg/constants/constants.go b/cli/pkg/constants/constants.go new file mode 100644 index 0000000..9e9e79e --- /dev/null +++ b/cli/pkg/constants/constants.go @@ -0,0 +1,17 @@ +package constants + +import "github.com/spf13/cobra" + +var ( + SCHEMA_COMMAND = cobra.Command{ + Use: "schema", + Aliases: []string{"schemas"}, + } + + RESOLVER_MAP_COMMAND = cobra.Command{ + Use: "resolvermap", + Aliases: []string{"r", "rm", "resolvermaps"}, + } + + +) diff --git a/cli/pkg/flagutils/common.go b/cli/pkg/flagutils/common.go new file mode 100644 index 0000000..a265598 --- /dev/null +++ b/cli/pkg/flagutils/common.go @@ -0,0 +1,24 @@ +package flagutils + +import ( + "github.com/solo-io/sqoop/cli/pkg/options" + "github.com/spf13/pflag" +) + +func AddOutputFlag(set *pflag.FlagSet, strptr *string) { + set.StringVarP(strptr, "output", "o", "", "output format: (yaml, json, table)") +} + +func AddFileFlag(set *pflag.FlagSet, strptr *string) { + set.StringVarP(strptr, "file", "f", "", "file to be read or written to") +} + +func AddInteractiveFlag(set *pflag.FlagSet, boolptr *bool) { + set.BoolVarP(boolptr, "interactive", "i", false, "interactive mode") +} + +func AddCommonFlags(set *pflag.FlagSet, opts *options.Top) { + AddOutputFlag(set, &opts.Output) + AddFileFlag(set, &opts.File) + AddInteractiveFlag(set, &opts.Interactive) +} diff --git a/cli/pkg/flagutils/install.go b/cli/pkg/flagutils/install.go new file mode 100644 index 0000000..d78bf10 --- /dev/null +++ b/cli/pkg/flagutils/install.go @@ -0,0 +1,17 @@ +package flagutils + +import ( + "github.com/solo-io/sqoop/pkg/defaults" + "github.com/solo-io/sqoop/version" + "github.com/solo-io/sqoop/cli/pkg/options" + "github.com/spf13/pflag" +) + +func AddInstallFlags(set *pflag.FlagSet, install *options.Install) { + set.BoolVarP(&install.DryRun, "dry-run", "d", false, "Dump the raw installation yaml instead of applying it to kubernetes") + if !version.IsReleaseVersion() { + set.StringVar(&install.ReleaseVersion, "release", "", "install using this release version. defaults to the latest github release") + } + set.StringVarP(&install.ManifestOverride, "file", "f", "", "Install sqoop from this kubernetes manifest yaml file rather than from a release") + set.StringVarP(&install.Namespace, "namespace", "n", defaults.GlooSystem, "which namespace to install sqoop into") +} \ No newline at end of file diff --git a/cli/pkg/flagutils/metadata.go b/cli/pkg/flagutils/metadata.go new file mode 100644 index 0000000..ecfbad5 --- /dev/null +++ b/cli/pkg/flagutils/metadata.go @@ -0,0 +1,19 @@ +package flagutils + +import ( +"github.com/solo-io/solo-kit/pkg/api/v1/resources/core" +"github.com/spf13/pflag" +) + +func AddMetadataFlags(set *pflag.FlagSet, metaptr *core.Metadata) { + addNameFlag(set, &metaptr.Name) + AddNamespaceFlag(set, &metaptr.Namespace) +} + +func addNameFlag(set *pflag.FlagSet, strptr *string) { + set.StringVar(strptr, "name", "", "name of the resource to read or write") +} + +func AddNamespaceFlag(set *pflag.FlagSet, strptr *string) { + set.StringVarP(strptr, "namespace", "n", "gloo-system", "namespace for reading or writing resources") +} diff --git a/cli/pkg/flagutils/resolver_map.go b/cli/pkg/flagutils/resolver_map.go new file mode 100644 index 0000000..fabbf1b --- /dev/null +++ b/cli/pkg/flagutils/resolver_map.go @@ -0,0 +1,16 @@ +package flagutils + +import ( + "github.com/solo-io/sqoop/cli/pkg/options" + "github.com/spf13/pflag" +) + +func AddResolverMapFlags(set *pflag.FlagSet, opts *options.ResolverMap) { + set.StringVarP(&opts.Upstream, "upstream", "u", "", "upstream where the function lives") + set.StringVarP(&opts.Function, "function", "g", "","function to use as resolver") + set.StringVarP(&opts.RequestTemplate, "request-template", "b", "", "template to use for the request body") + set.StringVarP(&opts.ResponseTemplate, "response-template", "r", "", "template to use for the response body") + set.StringVarP(&opts.SchemaName, "schema", "s", "", "name of the "+ + "schema to connect this resolver to. this is required if more than one schema contains a definition for the "+ + "type name.") +} diff --git a/cli/pkg/helpers/clients.go b/cli/pkg/helpers/clients.go new file mode 100644 index 0000000..3cb39bc --- /dev/null +++ b/cli/pkg/helpers/clients.go @@ -0,0 +1,144 @@ +package helpers + +import ( + "context" + "fmt" + "github.com/solo-io/sqoop/pkg/api/v1" + "time" + + glooV1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1" + "github.com/solo-io/solo-kit/pkg/api/v1/clients/factory" + "github.com/solo-io/solo-kit/pkg/api/v1/clients/kube" + "github.com/solo-io/solo-kit/pkg/api/v1/clients/memory" + "github.com/solo-io/solo-kit/pkg/errors" + "github.com/solo-io/solo-kit/pkg/utils/kubeutils" + "github.com/solo-io/solo-kit/pkg/utils/log" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" +) + +var memoryResourceClient *factory.MemoryResourceClientFactory + +func UseMemoryClients() { + memoryResourceClient = &factory.MemoryResourceClientFactory{ + Cache: memory.NewInMemoryResourceCache(), + } +} + + +func MustSchemaClient() v1.SchemaClient { + client, err := SchemaClient() + if err != nil { + log.Fatalf("failed to create schema client: %v", err) + } + return client +} + +func SchemaClient() (v1.SchemaClient, error) { + if memoryResourceClient != nil { + return v1.NewSchemaClient(memoryResourceClient) + } + + cfg, err := kubeutils.GetConfig("", "") + if err != nil { + return nil, errors.Wrapf(err, "getting kube config") + } + cache := kube.NewKubeCache(context.TODO()) + schemaClient, err := v1.NewSchemaClient(&factory.KubeResourceClientFactory{ + Crd: v1.SchemaCrd, + Cfg: cfg, + SharedCache: cache, + }) + if err != nil { + return nil, errors.Wrapf(err, "creating resolver map client") + } + if err := schemaClient.Register(); err != nil { + return nil, err + } + return schemaClient, nil +} + +func MustResolverMapClient() v1.ResolverMapClient { + client, err := ResolverMapClient() + if err != nil { + log.Fatalf("failed to create resolver map client: %v", err) + } + return client +} + +func ResolverMapClient() (v1.ResolverMapClient, error) { + if memoryResourceClient != nil { + return v1.NewResolverMapClient(memoryResourceClient) + } + + cfg, err := kubeutils.GetConfig("", "") + if err != nil { + return nil, errors.Wrapf(err, "getting kube config") + } + cache := kube.NewKubeCache(context.TODO()) + resolverMapClient, err := v1.NewResolverMapClient(&factory.KubeResourceClientFactory{ + Crd: v1.ResolverMapCrd, + Cfg: cfg, + SharedCache: cache, + }) + if err != nil { + return nil, errors.Wrapf(err, "creating resolver map client") + } + if err := resolverMapClient.Register(); err != nil { + return nil, err + } + return resolverMapClient, nil +} + +func MustSettingsClient() glooV1.SettingsClient { + client, err := SettingsClient() + if err != nil { + log.Fatalf("failed to create settings client: %v", err) + } + return client +} + +func SettingsClient() (glooV1.SettingsClient, error) { + if memoryResourceClient != nil { + return glooV1.NewSettingsClient(memoryResourceClient) + } + + cfg, err := kubeutils.GetConfig("", "") + if err != nil { + return nil, errors.Wrapf(err, "getting kube config") + } + cache := kube.NewKubeCache(context.TODO()) + settingsClient, err := glooV1.NewSettingsClient(&factory.KubeResourceClientFactory{ + Crd: glooV1.SettingsCrd, + Cfg: cfg, + SharedCache: cache, + }) + if err != nil { + return nil, errors.Wrapf(err, "creating settings client") + } + if err := settingsClient.Register(); err != nil { + return nil, err + } + return settingsClient, nil +} + +func getKubernetesClient() (*kubernetes.Clientset, error) { + config, err := getKubernetesConfig(0) + if err != nil { + return nil, err + } + kubeClient, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, err + } + return kubeClient, nil +} + +func getKubernetesConfig(timeout time.Duration) (*rest.Config, error) { + config, err := kubeutils.GetConfig("", "") + if err != nil { + return nil, fmt.Errorf("Error retrieving Kubernetes configuration: %v \n", err) + } + config.Timeout = timeout + return config, nil +} diff --git a/cli/pkg/options/types.go b/cli/pkg/options/types.go new file mode 100644 index 0000000..5685a81 --- /dev/null +++ b/cli/pkg/options/types.go @@ -0,0 +1,44 @@ +package options + +import ( + "context" + "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" +) + +type Options struct { + Metadata core.Metadata + Top Top + Install Install + Schema Schema + ResolverMap ResolverMap +} + +type Top struct { + Interactive bool + File string + Output string + Namesapce string + Ctx context.Context +} + +type Install struct { + DryRun bool + ReleaseVersion string + ManifestOverride string + Namespace string +} + +type Schema struct { + Name string + ResolverMap string +} + +type ResolverMap struct { + Upstream string + Function string + RequestTemplate string + ResponseTemplate string + SchemaName string + TypeName string + FieldName string +} diff --git a/cli/pkg/testutils/main.go b/cli/pkg/testutils/main.go new file mode 100644 index 0000000..dd0f8a8 --- /dev/null +++ b/cli/pkg/testutils/main.go @@ -0,0 +1,78 @@ +package testutils + +import ( + "bytes" + "io" + "io/ioutil" + "os" + "os/exec" + "strings" + + "github.com/pkg/errors" + + "github.com/solo-io/solo-kit/pkg/utils/log" + "github.com/solo-io/sqoop/cli/pkg/cmd" +) + +func Sqoopctl(args string) error { + app := cmd.App("test") + app.SetArgs(strings.Split(args, " ")) + return app.Execute() +} +func SqoopctlOut(args string) (string, error) { + stdOut := os.Stdout + r, w, err := os.Pipe() + if err != nil { + return "", err + } + os.Stdout = w + + app := cmd.App("test") + app.SetArgs(strings.Split(args, " ")) + err = app.Execute() + + outC := make(chan string) + + // copy the output in a separate goroutine so printing can't block indefinitely + go func() { + var buf bytes.Buffer + io.Copy(&buf, r) + outC <- buf.String() + }() + + // back to normal state + w.Close() + os.Stdout = stdOut // restoring the real stdout + out := <-outC + + return strings.TrimSuffix(out, "\n"), nil +} + +func Make(dir, args string) error { + make := exec.Command("make", strings.Split(args, " ")...) + make.Dir = dir + out, err := make.CombinedOutput() + if err != nil { + return errors.Errorf("make failed with err: %s", out) + } + return nil +} + +func MustWriteTestFile(contents string) string { + tmpFile, err := ioutil.TempFile("", "test-") + + if err != nil { + log.Fatalf("Failed to create test file: %v", err) + } + + text := []byte(contents) + if _, err = tmpFile.Write(text); err != nil { + log.Fatalf("Failed to write to test file: %v", err) + } + + if err := tmpFile.Close(); err != nil { + log.Fatalf("Failed to write to test file: %v", err) + } + + return tmpFile.Name() +} diff --git a/cloudbuild-cache.yaml b/cloudbuild-cache.yaml new file mode 100644 index 0000000..9f41c81 --- /dev/null +++ b/cloudbuild-cache.yaml @@ -0,0 +1,15 @@ +steps: +- name: 'gcr.io/$PROJECT_ID/dep' + args: ['ensure'] + env: + - 'PROJECT_ROOT=github.com/solo-io/sqoop' + +- name: 'gcr.io/$PROJECT_ID/go-make' + entrypoint: 'bash' + args: ['-c', 'tar -zvcf sqoop-dep.tar.gz ./gopath/pkg/dep'] + env: + - 'PROJECT_ROOT=github.com/solo-io/sqoop' + - 'GOPATH=/workspace/gopath' + +- name: gcr.io/cloud-builders/gsutil + args: ['cp', 'sqoop-dep.tar.gz', 'gs://solo-public-cache/sqoop/sqoop-dep.tar.gz'] \ No newline at end of file diff --git a/cloudbuild.yaml b/cloudbuild.yaml new file mode 100644 index 0000000..cabfb07 --- /dev/null +++ b/cloudbuild.yaml @@ -0,0 +1,109 @@ +steps: + + - name: 'gcr.io/cloud-builders/wget' + entrypoint: ./ci/spell.sh + args: ['check'] + waitFor: ['-'] + env: + - 'LSCOMMAND=find * -path gopath -prune -o -print' + id: 'check-spelling' + # Can create a dep cache by running 'gcloud builds submit . --config=cloudbuild-cache.yaml' + # These two steps should populate the cache + - name: gcr.io/cloud-builders/gsutil + entrypoint: 'bash' + args: ['-c', 'mkdir -p ./gopath/pkg/dep && gsutil cat gs://solo-public-cache/sqoop/sqoop-dep.tar.gz | tar -xzf -'] + id: 'download-untar-dep-cache' + waitFor: ['-'] + + # Run dep to set up the project so it can be built and tested + # Dep modifies workspace so that the code is moved into the PROJECT_ROOT + # All steps after this must set GOPATH + - name: 'gcr.io/$PROJECT_ID/dep' + args: ['ensure'] + env: + - 'PROJECT_ROOT=github.com/solo-io/sqoop' + - 'GIT_SSH_CONFIG=FALSE' + id: 'dep' + waitFor: ['download-untar-dep-cache'] + + # Helm setup + # 1) Create directory to sync helm data + - name: gcr.io/cloud-builders/gsutil + entrypoint: mkdir + args: ['-p', './_output/helm'] + dir: './gopath/src/github.com/solo-io/sqoop' + waitFor: ['dep'] + id: 'make-helm-dir' + - name: gcr.io/cloud-builders/gsutil + args: + - rsync + - -r + - gs://sqoop-helm/ + - './_output/helm' + dir: './gopath/src/github.com/solo-io/sqoop' + waitFor: ['make-helm-dir'] + id: 'setup-helm' + + # run all of the tests + - name: 'gcr.io/$PROJECT_ID/e2e-ginkgo' + env: + - 'PROJECT_ROOT=github.com/solo-io/sqoop' + - 'GOPATH=/workspace/gopath' + - 'CLOUDSDK_COMPUTE_ZONE=us-central1-a' + - 'CLOUDSDK_CONTAINER_CLUSTER=test-cluster' + dir: './gopath/src/github.com/solo-io/sqoop' + args: ['-r', '-failFast', '-p'] + waitFor: ['setup-helm'] + id: 'test' + + # Build everything + # 1) Compile all docker images + # 2) Run make targets to push docker images, compile manifests, produce release artifacts, deploy docs + # 3) Publish helm chart + - name: 'gcr.io/cloud-builders/docker' + entrypoint: 'bash' + args: ['-c', 'docker login --username soloiobot --password $$DOCKER_HUB_PASSWORD'] + secretEnv: ['DOCKER_HUB_PASSWORD'] + id: 'docker-login' + waitFor: ['test'] + - name: 'gcr.io/$PROJECT_ID/go-make' + args: ['docker'] + env: + - 'PROJECT_ROOT=github.com/solo-io/sqoop' + - 'GOPATH=/workspace/gopath' + - 'TAGGED_VERSION=$TAG_NAME' + dir: './gopath/src/github.com/solo-io/sqoop' + waitFor: ['docker-login'] + id: 'compile' + - name: 'gcr.io/$PROJECT_ID/go-make' + args: ['docker-push', 'manifest', 'release'] + env: + - 'TAGGED_VERSION=$TAG_NAME' + - 'PROJECT_ROOT=github.com/solo-io/sqoop' + - 'GOPATH=/workspace/gopath' + - 'HELM_HOME=/root/.helm' # tell helm where to find data + dir: './gopath/src/github.com/solo-io/sqoop' + secretEnv: ['GITHUB_TOKEN', 'FIREBASE_TOKEN'] + id: 'release' + waitFor: ['compile'] + + + # Sync helm chart data back to google storage bucket + - name: gcr.io/cloud-builders/gsutil + args: + - rsync + - -r + - './_output/helm' + - gs://sqoop-helm/ + dir: './gopath/src/github.com/solo-io/sqoop' + waitFor: ['release'] + id: 'set-helm-chart' + +secrets: + - kmsKeyName: projects/solo-public/locations/global/keyRings/build/cryptoKeys/build-key + secretEnv: + GITHUB_TOKEN: CiQABlzmSRpjt9c2jcCGU2lIk68qAkHIzIHUeYS+artlcens/7oSUQCCPGSG407g5usGvAhM+oL98Xir0fHWUiNe3827h9zdhmkCbrZpNqfVFkMhAxQ/ZlhC31+KwzWoHnDSb3RN7CoKj+gves6q7MMf7wNxSmC46A== + DOCKER_HUB_PASSWORD: CiQABlzmSW0u+qhXDvTCxLnbi09Zm88eCU0wSdvFn1W+6WOpTgQSTgCCPGSGTAlMndrEkYOynPhDzTXrW1q1eAsQYjKOWOwZKodcQZ2WIzdvpOOjv+WrGTssWWg1uPFV4CnajT7DzeNAb7USkla1epatm6OnuQ== + FIREBASE_TOKEN: CiQABlzmSc0BWpPfrGRtDscrxOfp9ZBkZO9fkO79tjEmA14c8ZESVwCCPGSG8uZtLSmFucmEEJGJ0080ON7Zw5TjLe2YdwuxnSOA5YzZryVwLFAMzRmfb6OBxyThTZKvGZzgfXyv6CeLwYX0exk20u7k2bnrWbFHO0Aa4TiQqw== + +timeout: 6600s diff --git a/cmd/Dockerfile b/cmd/Dockerfile new file mode 100644 index 0000000..1b2a7f1 --- /dev/null +++ b/cmd/Dockerfile @@ -0,0 +1,9 @@ +FROM alpine + +RUN apk upgrade --update-cache \ + && apk add ca-certificates \ + && rm -rf /var/cache/apk/* + +COPY sqoop-linux-amd64 /usr/local/bin/sqoop + +ENTRYPOINT ["/usr/local/bin/sqoop"] \ No newline at end of file diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..8c947eb --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,22 @@ +package main + +import ( + "os" + + "github.com/solo-io/go-utils/stats" + "github.com/solo-io/solo-kit/pkg/utils/log" + "github.com/solo-io/sqoop/pkg/setup" +) + +const ( + START_STATS_SERVER = "START_STATS_SERVER" +) + +func main() { + if os.Getenv(START_STATS_SERVER) != "" { + stats.StartStatsServer() + } + if err := setup.Main(); err != nil { + log.Fatalf("err in main: %v", err.Error()) + } +} diff --git a/cmd/sqoop/Dockerfile b/cmd/sqoop/Dockerfile deleted file mode 100644 index 6b6d5ec..0000000 --- a/cmd/sqoop/Dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -FROM alpine:3.7 -COPY sqoop /sqoop -EXPOSE 9090 -ENTRYPOINT ["/sqoop"] diff --git a/cmd/sqoop/Dockerfile.debug b/cmd/sqoop/Dockerfile.debug deleted file mode 100644 index 1959786..0000000 --- a/cmd/sqoop/Dockerfile.debug +++ /dev/null @@ -1,4 +0,0 @@ -FROM ubuntu -COPY sqoop-debug /sqoop -EXPOSE 9090 -ENTRYPOINT ["/sqoop"] diff --git a/cmd/sqoop/main.go b/cmd/sqoop/main.go deleted file mode 100644 index 393c3e1..0000000 --- a/cmd/sqoop/main.go +++ /dev/null @@ -1,49 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "github.com/pkg/errors" - glooflags "github.com/solo-io/gloo/pkg/bootstrap/flags" - "github.com/solo-io/gloo/pkg/log" - "github.com/solo-io/gloo/pkg/signals" - "github.com/solo-io/sqoop/pkg/bootstrap" - "github.com/solo-io/sqoop/pkg/bootstrap/flags" - "github.com/solo-io/sqoop/pkg/core" - "github.com/spf13/cobra" -) - -func main() { - if err := rootCmd.Execute(); err != nil { - fmt.Println(err) - os.Exit(1) - } -} - -var opts bootstrap.Options - -var rootCmd = &cobra.Command{ - Use: "sqoop", - Short: "runs sqoop", - RunE: func(cmd *cobra.Command, args []string) error { - eventLoop, err := core.Setup(opts) - if err != nil { - return errors.Wrap(err, "setting up event loop") - } - - stop := signals.SetupSignalHandler() - eventLoop.Run(stop) - - log.Printf("shutting down Sqoop") - return nil - }, -} - -func init() { - glooflags.AddConfigStorageOptionFlags(rootCmd, &opts.Options) - glooflags.AddFileFlags(rootCmd, &opts.Options) - glooflags.AddKubernetesFlags(rootCmd, &opts.Options) - glooflags.AddConsulFlags(rootCmd, &opts.Options) - flags.AddSqoopFlags(rootCmd, &opts) -} diff --git a/cmd/sqoopctl/main.go b/cmd/sqoopctl/main.go deleted file mode 100644 index 841c924..0000000 --- a/cmd/sqoopctl/main.go +++ /dev/null @@ -1,18 +0,0 @@ -package main - -import ( - "fmt" - "os" - - _ "github.com/solo-io/sqoop/pkg/sqoopctl/install" - _ "github.com/solo-io/sqoop/pkg/sqoopctl/resolvermap" - _ "github.com/solo-io/sqoop/pkg/sqoopctl/schema" - "github.com/solo-io/sqoop/pkg/sqoopctl" -) - -func main() { - if err := sqoopctl.RootCmd.Execute(); err != nil { - fmt.Println(err) - os.Exit(1) - } -} diff --git a/docs/QLoo.png b/docs/QLoo.png deleted file mode 100644 index eeb66e93ab3a326ef2ff2504751d27d107e4a6b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 107552 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\Ml9%7Q&YnZ?hxviXZg{l`7vWfLA1!(W{GBYHBF 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

k2hFy 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�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 @@ -15,12 +19,9 @@ for REST/gRPC applications and serverless functions. Sqoop routes requests to da Envoy [HTTP filters](https://envoyproxy.io/docs/envoy/latest/intro/arch_overview/http_filters.html) for security, load balancing, and [more](https://gloo.solo.io/#features). -
-

-Sqoop -

-
- +
Sqoop
+ + ## Workflow with Sqoop * Register or Discovery API Endpoints and Serverless Functions with Gloo * Upload a GraphQL schema diff --git a/docs/api.json b/docs/api.json deleted file mode 100644 index ba3bb9e..0000000 --- a/docs/api.json +++ /dev/null @@ -1,635 +0,0 @@ -{ - "files": [ - { - "name": "config.proto", - "description": "", - "package": "sqoop.api.v1", - "hasEnums": false, - "hasExtensions": false, - "hasMessages": true, - "hasServices": false, - "enums": [], - "extensions": [], - "messages": [ - { - "name": "Config", - "longName": "Config", - "fullName": "sqoop.api.v1.Config", - "description": "Config is a top-level config object. It is used internally by Sqoop as a container for the entire set of config objects.", - "hasExtensions": false, - "hasFields": true, - "extensions": [], - "fields": [ - { - "name": "schemas", - "description": "the set of all schemas defined by the user", - "label": "repeated", - "type": "Schema", - "longType": "Schema", - "fullType": "sqoop.api.v1.Schema", - "defaultValue": "" - }, - { - "name": "resolver_maps", - "description": "the set of all resolver maps defined by the user", - "label": "repeated", - "type": "ResolverMap", - "longType": "ResolverMap", - "fullType": "sqoop.api.v1.ResolverMap", - "defaultValue": "" - } - ] - } - ], - "services": [] - }, - { - "name": "resolver_map.proto", - "description": "", - "package": "sqoop.api.v1", - "hasEnums": false, - "hasExtensions": false, - "hasMessages": true, - "hasServices": false, - "enums": [], - "extensions": [], - "messages": [ - { - "name": "ResolverMap", - "longName": "ResolverMap", - "fullName": "sqoop.api.v1.ResolverMap", - "description": "The ResolverMap object maps Resolvers to the fields in the GraphQL Schema\nThe skeleton of a Resolver Map will be generated by Sqoop automatically when a schema\nis read or updated if one does not alreay exist.", - "hasExtensions": false, - "hasFields": true, - "extensions": [], - "fields": [ - { - "name": "name", - "description": "Name of the Resolver Map. Resolver Map names must be unique\n\nResolver Map Names must be unique and follow the following syntax rules:\nOne or more lowercase rfc1035/rfc1123 labels separated by '.' with a maximum length of 253 characters.", - "label": "", - "type": "string", - "longType": "string", - "fullType": "string", - "defaultValue": "" - }, - { - "name": "types", - "description": "Types is a map of Type Names (defined in the schema) to a TypeResolver, which contain resolvers for the\nspecific fields of the type", - "label": "repeated", - "type": "TypesEntry", - "longType": "ResolverMap.TypesEntry", - "fullType": "sqoop.api.v1.ResolverMap.TypesEntry", - "defaultValue": "" - }, - { - "name": "status", - "description": "Status indicates the validation status of the role resource.\nStatus is read-only by clients, and set by gloo during validation", - "label": "", - "type": "Status", - "longType": "gloo.api.v1.Status", - "fullType": "gloo.api.v1.Status", - "defaultValue": "" - }, - { - "name": "metadata", - "description": "Metadata contains the resource metadata for the role", - "label": "", - "type": "Metadata", - "longType": "gloo.api.v1.Metadata", - "fullType": "gloo.api.v1.Metadata", - "defaultValue": "" - } - ] - }, - { - "name": "TypesEntry", - "longName": "ResolverMap.TypesEntry", - "fullName": "sqoop.api.v1.ResolverMap.TypesEntry", - "description": "", - "hasExtensions": false, - "hasFields": true, - "extensions": [], - "fields": [ - { - "name": "key", - "description": "", - "label": "", - "type": "string", - "longType": "string", - "fullType": "string", - "defaultValue": "" - }, - { - "name": "value", - "description": "", - "label": "", - "type": "TypeResolver", - "longType": "TypeResolver", - "fullType": "sqoop.api.v1.TypeResolver", - "defaultValue": "" - } - ] - }, - { - "name": "TypeResolver", - "longName": "TypeResolver", - "fullName": "sqoop.api.v1.TypeResolver", - "description": "TypeResolver contains the individual resolvers for each field for a specific type", - "hasExtensions": false, - "hasFields": true, - "extensions": [], - "fields": [ - { - "name": "fields", - "description": "This is a map of Field Names to the resolver that Sqoop should invoke when a query arrives for that field", - "label": "repeated", - "type": "FieldsEntry", - "longType": "TypeResolver.FieldsEntry", - "fullType": "sqoop.api.v1.TypeResolver.FieldsEntry", - "defaultValue": "" - } - ] - }, - { - "name": "FieldsEntry", - "longName": "TypeResolver.FieldsEntry", - "fullName": "sqoop.api.v1.TypeResolver.FieldsEntry", - "description": "", - "hasExtensions": false, - "hasFields": true, - "extensions": [], - "fields": [ - { - "name": "key", - "description": "", - "label": "", - "type": "string", - "longType": "string", - "fullType": "string", - "defaultValue": "" - }, - { - "name": "value", - "description": "", - "label": "", - "type": "Resolver", - "longType": "Resolver", - "fullType": "sqoop.api.v1.Resolver", - "defaultValue": "" - } - ] - }, - { - "name": "Resolver", - "longName": "Resolver", - "fullName": "sqoop.api.v1.Resolver", - "description": "Resolvers define the actual logic Sqoop needs to know in order to resolve a specific field query", - "hasExtensions": false, - "hasFields": true, - "extensions": [], - "fields": [ - { - "name": "gloo_resolver", - "description": "a GlooResolver, which leverages Gloo to retrieve data from backend services and functions for the query", - "label": "", - "type": "GlooResolver", - "longType": "GlooResolver", - "fullType": "sqoop.api.v1.GlooResolver", - "defaultValue": "" - }, - { - "name": "template_resolver", - "description": "a TemplateResolver, which uses Go Templates to generate data for the query", - "label": "", - "type": "TemplateResolver", - "longType": "TemplateResolver", - "fullType": "sqoop.api.v1.TemplateResolver", - "defaultValue": "" - }, - { - "name": "nodejs_resolver", - "description": "a NodeJSResolver, which calls NodeJS functions to return data for the query", - "label": "", - "type": "NodeJSResolver", - "longType": "NodeJSResolver", - "fullType": "sqoop.api.v1.NodeJSResolver", - "defaultValue": "" - } - ] - }, - { - "name": "GlooResolver", - "longName": "GlooResolver", - "fullName": "sqoop.api.v1.GlooResolver", - "description": "GlooResolvers are the \"meat\" of Sqoop. GlooResolvers tell Sqoop how to invoke a \"Gloo Function\"", - "hasExtensions": false, - "hasFields": true, - "extensions": [], - "fields": [ - { - "name": "request_template", - "description": "the Request Template, if specified, will become the body of the HTTP request used to invoke a function through Gloo\ninput parameters, if needed, should be specified in the request template. See Sqoop's [Resolver documentation](TODO)\nfor more information on Request Templates.", - "label": "", - "type": "string", - "longType": "string", - "fullType": "string", - "defaultValue": "" - }, - { - "name": "response_template", - "description": "The response template, if specified, will transform the body of HTTP responses returned by Gloo functions.\nThis field should be used if the object returned by the Gloo Function does not match the type specified in the GraphQL schema.\nIt can also be used to modify or transform responses from their original state. See Sqoop's [Resolver documentation](TODO)\nfor more information on Response Templates.", - "label": "", - "type": "string", - "longType": "string", - "fullType": "string", - "defaultValue": "" - }, - { - "name": "content_type", - "description": "Optional. Use to set the outbound HTTP Request header `Content-Type`. Defaults to `application/json`", - "label": "", - "type": "string", - "longType": "string", - "fullType": "string", - "defaultValue": "" - }, - { - "name": "single_function", - "description": "SingleFunction specifies this resolver will always invoke a single function.", - "label": "", - "type": "Function", - "longType": "Function", - "fullType": "sqoop.api.v1.Function", - "defaultValue": "" - }, - { - "name": "multi_function", - "description": "MultiFunction specifies the resolver will distribute invocation across multiple functions", - "label": "", - "type": "MultiFunction", - "longType": "MultiFunction", - "fullType": "sqoop.api.v1.MultiFunction", - "defaultValue": "" - } - ] - }, - { - "name": "Function", - "longName": "Function", - "fullName": "sqoop.api.v1.Function", - "description": "A reference to a function known to Gloo", - "hasExtensions": false, - "hasFields": true, - "extensions": [], - "fields": [ - { - "name": "upstream", - "description": "Name of the Gloo Upstream that provides this function", - "label": "", - "type": "string", - "longType": "string", - "fullType": "string", - "defaultValue": "" - }, - { - "name": "function", - "description": "Name of the function itself. See Gloo documentation for more details on functions in Gloo", - "label": "", - "type": "string", - "longType": "string", - "fullType": "string", - "defaultValue": "" - } - ] - }, - { - "name": "MultiFunction", - "longName": "MultiFunction", - "fullName": "sqoop.api.v1.MultiFunction", - "description": "A reference to a list of functions known to Gloo", - "hasExtensions": false, - "hasFields": true, - "extensions": [], - "fields": [ - { - "name": "weighted_functions", - "description": "A list of functions with weights. Must have size \u003e= 1", - "label": "repeated", - "type": "WeightedFunction", - "longType": "WeightedFunction", - "fullType": "sqoop.api.v1.WeightedFunction", - "defaultValue": "" - } - ] - }, - { - "name": "WeightedFunction", - "longName": "WeightedFunction", - "fullName": "sqoop.api.v1.WeightedFunction", - "description": "", - "hasExtensions": false, - "hasFields": true, - "extensions": [], - "fields": [ - { - "name": "function", - "description": "the function to call", - "label": "", - "type": "Function", - "longType": "Function", - "fullType": "sqoop.api.v1.Function", - "defaultValue": "" - }, - { - "name": "weight", - "description": "Invoking each functoion will be balanced by the ratio of the function's weight to the total weight on a resolver", - "label": "", - "type": "uint32", - "longType": "uint32", - "fullType": "uint32", - "defaultValue": "" - } - ] - }, - { - "name": "TemplateResolver", - "longName": "TemplateResolver", - "fullName": "sqoop.api.v1.TemplateResolver", - "description": "A Go-template which will return data for a Resolver without making a function call. Template Resolvers can make use\nof Sqoop's builtin template functions as well as the data provided by the Params object to the resolver.\nRead more about Templates and Resolvers in Sqoop's [Resolver documentation](TODO).", - "hasExtensions": false, - "hasFields": true, - "extensions": [], - "fields": [ - { - "name": "inline_template", - "description": "the Go template as an inline string", - "label": "", - "type": "string", - "longType": "string", - "fullType": "string", - "defaultValue": "" - } - ] - }, - { - "name": "NodeJSResolver", - "longName": "NodeJSResolver", - "fullName": "sqoop.api.v1.NodeJSResolver", - "description": "NOTE: currently unsupported", - "hasExtensions": false, - "hasFields": true, - "extensions": [], - "fields": [ - { - "name": "inline_code", - "description": "", - "label": "", - "type": "string", - "longType": "string", - "fullType": "string", - "defaultValue": "" - } - ] - } - ], - "services": [] - }, - { - "name": "schema.proto", - "description": "", - "package": "sqoop.api.v1", - "hasEnums": false, - "hasExtensions": false, - "hasMessages": true, - "hasServices": false, - "enums": [], - "extensions": [], - "messages": [ - { - "name": "Schema", - "longName": "Schema", - "fullName": "sqoop.api.v1.Schema", - "description": "The Schema object wraps the user's GraphQL Schema, which is stored as an inline string.\nThe Schema Object contains a Status field which is used by Sqoop to validate the user's input schema.", - "hasExtensions": false, - "hasFields": true, - "extensions": [], - "fields": [ - { - "name": "name", - "description": "Schema Names must be unique and follow the following syntax rules:\nOne or more lowercase rfc1035/rfc1123 labels separated by '.' with a maximum length of 253 characters.", - "label": "", - "type": "string", - "longType": "string", - "fullType": "string", - "defaultValue": "" - }, - { - "name": "resolver_map", - "description": "name of the resolver map to use to resolve this schema.\nif the user leaves this empty, Sqoop will generate the skeleton of a resolver map for the user", - "label": "", - "type": "string", - "longType": "string", - "fullType": "string", - "defaultValue": "" - }, - { - "name": "inline_schema", - "description": "inline the entire graphql schema as a string here", - "label": "", - "type": "string", - "longType": "string", - "fullType": "string", - "defaultValue": "" - }, - { - "name": "status", - "description": "Status indicates the validation status of the role resource.\nStatus is read-only by clients, and set by gloo during validation", - "label": "", - "type": "Status", - "longType": "gloo.api.v1.Status", - "fullType": "gloo.api.v1.Status", - "defaultValue": "" - }, - { - "name": "metadata", - "description": "Metadata contains the resource metadata for the role", - "label": "", - "type": "Metadata", - "longType": "gloo.api.v1.Metadata", - "fullType": "gloo.api.v1.Metadata", - "defaultValue": "" - } - ] - } - ], - "services": [] - } - ], - "scalarValueTypes": [ - { - "protoType": "double", - "notes": "", - "cppType": "double", - "csType": "double", - "goType": "float64", - "javaType": "double", - "phpType": "float", - "pythonType": "float", - "rubyType": "Float" - }, - { - "protoType": "float", - "notes": "", - "cppType": "float", - "csType": "float", - "goType": "float32", - "javaType": "float", - "phpType": "float", - "pythonType": "float", - "rubyType": "Float" - }, - { - "protoType": "int32", - "notes": "Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead.", - "cppType": "int32", - "csType": "int", - "goType": "int32", - "javaType": "int", - "phpType": "integer", - "pythonType": "int", - "rubyType": "Bignum or Fixnum (as required)" - }, - { - "protoType": "int64", - "notes": "Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead.", - "cppType": "int64", - "csType": "long", - "goType": "int64", - "javaType": "long", - "phpType": "integer/string", - "pythonType": "int/long", - "rubyType": "Bignum" - }, - { - "protoType": "uint32", - "notes": "Uses variable-length encoding.", - "cppType": "uint32", - "csType": "uint", - "goType": "uint32", - "javaType": "int", - "phpType": "integer", - "pythonType": "int/long", - "rubyType": "Bignum or Fixnum (as required)" - }, - { - "protoType": "uint64", - "notes": "Uses variable-length encoding.", - "cppType": "uint64", - "csType": "ulong", - "goType": "uint64", - "javaType": "long", - "phpType": "integer/string", - "pythonType": "int/long", - "rubyType": "Bignum or Fixnum (as required)" - }, - { - "protoType": "sint32", - "notes": "Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s.", - "cppType": "int32", - "csType": "int", - "goType": "int32", - "javaType": "int", - "phpType": "integer", - "pythonType": "int", - "rubyType": "Bignum or Fixnum (as required)" - }, - { - "protoType": "sint64", - "notes": "Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s.", - "cppType": "int64", - "csType": "long", - "goType": "int64", - "javaType": "long", - "phpType": "integer/string", - "pythonType": "int/long", - "rubyType": "Bignum" - }, - { - "protoType": "fixed32", - "notes": "Always four bytes. More efficient than uint32 if values are often greater than 2^28.", - "cppType": "uint32", - "csType": "uint", - "goType": "uint32", - "javaType": "int", - "phpType": "integer", - "pythonType": "int", - "rubyType": "Bignum or Fixnum (as required)" - }, - { - "protoType": "fixed64", - "notes": "Always eight bytes. More efficient than uint64 if values are often greater than 2^56.", - "cppType": "uint64", - "csType": "ulong", - "goType": "uint64", - "javaType": "long", - "phpType": "integer/string", - "pythonType": "int/long", - "rubyType": "Bignum" - }, - { - "protoType": "sfixed32", - "notes": "Always four bytes.", - "cppType": "int32", - "csType": "int", - "goType": "int32", - "javaType": "int", - "phpType": "integer", - "pythonType": "int", - "rubyType": "Bignum or Fixnum (as required)" - }, - { - "protoType": "sfixed64", - "notes": "Always eight bytes.", - "cppType": "int64", - "csType": "long", - "goType": "int64", - "javaType": "long", - "phpType": "integer/string", - "pythonType": "int/long", - "rubyType": "Bignum" - }, - { - "protoType": "bool", - "notes": "", - "cppType": "bool", - "csType": "bool", - "goType": "bool", - "javaType": "boolean", - "phpType": "boolean", - "pythonType": "boolean", - "rubyType": "TrueClass/FalseClass" - }, - { - "protoType": "string", - "notes": "A string must always contain UTF-8 encoded or 7-bit ASCII text.", - "cppType": "string", - "csType": "string", - "goType": "string", - "javaType": "String", - "phpType": "string", - "pythonType": "str/unicode", - "rubyType": "String (UTF-8)" - }, - { - "protoType": "bytes", - "notes": "May contain any arbitrary sequence of bytes.", - "cppType": "string", - "csType": "ByteString", - "goType": "[]byte", - "javaType": "ByteString", - "phpType": "string", - "pythonType": "str", - "rubyType": "String (ASCII-8BIT)" - } - ] -} \ No newline at end of file diff --git a/docs/cli/_index.md b/docs/cli/_index.md new file mode 100644 index 0000000..7ce1220 --- /dev/null +++ b/docs/cli/_index.md @@ -0,0 +1,4 @@ +--- +title: "sqoopctl" +weight: 5 +--- diff --git a/docs/cli/sqoopctl.md b/docs/cli/sqoopctl.md new file mode 100644 index 0000000..7164190 --- /dev/null +++ b/docs/cli/sqoopctl.md @@ -0,0 +1,36 @@ +--- +title: "sqoopctl" +weight: 5 +--- +## sqoopctl + +Interact with Sqoop's storage API from the command line + +### Synopsis + +As Sqoop features a storage-based API, direct communication with the Sqoop server is not necessary. sqoopctl simplifies the administration of Sqoop by providing an easy way to create, read, update, and delete Sqoop storage objects. + +The primary concerns of sqoopctl are Schemas and ResolverMaps. Schemas contain your GraphQL schema; ResolverMaps define how your schema fields are resolved. + +Start by creating a schema using sqoopctl schema create --from-file + +``` +sqoopctl [flags] +``` + +### Options + +``` + -f, --file string file to be read or written to + -h, --help help for sqoopctl + -i, --interactive interactive mode + -o, --output string output format: (yaml, json, table) +``` + +### SEE ALSO + +* [sqoopctl install](../sqoopctl_install) - install gloo on different platforms +* [sqoopctl resolvermap](../sqoopctl_resolvermap) - +* [sqoopctl schema](../sqoopctl_schema) - +* [sqoopctl uninstall](../sqoopctl_uninstall) - uninstall gloo + diff --git a/docs/cli/sqoopctl_install.md b/docs/cli/sqoopctl_install.md new file mode 100644 index 0000000..072867d --- /dev/null +++ b/docs/cli/sqoopctl_install.md @@ -0,0 +1,31 @@ +--- +title: "sqoopctl install" +weight: 5 +--- +## sqoopctl install + +install gloo on different platforms + +### Synopsis + +choose which version of Gloo to install. + +### Options + +``` + -h, --help help for install +``` + +### Options inherited from parent commands + +``` + -f, --file string file to be read or written to + -i, --interactive interactive mode + -o, --output string output format: (yaml, json, table) +``` + +### SEE ALSO + +* [sqoopctl](../sqoopctl) - Interact with Sqoop's storage API from the command line +* [sqoopctl install kube](../sqoopctl_install_kube) - install sqoop on kubernetes + diff --git a/docs/cli/sqoopctl_install_kube.md b/docs/cli/sqoopctl_install_kube.md new file mode 100644 index 0000000..62db25a --- /dev/null +++ b/docs/cli/sqoopctl_install_kube.md @@ -0,0 +1,37 @@ +--- +title: "sqoopctl install kube" +weight: 5 +--- +## sqoopctl install kube + +install sqoop on kubernetes + +### Synopsis + +requires kubectl to be installed + +``` +sqoopctl install kube [flags] +``` + +### Options + +``` + -d, --dry-run Dump the raw installation yaml instead of applying it to kubernetes + -h, --help help for kube + -n, --namespace string which namespace to install sqoop into (default "sqoop-system") + --release string install using this release version. defaults to the latest github release +``` + +### Options inherited from parent commands + +``` + -f, --file string file to be read or written to + -i, --interactive interactive mode + -o, --output string output format: (yaml, json, table) +``` + +### SEE ALSO + +* [sqoopctl install](../sqoopctl_install) - install gloo on different platforms + diff --git a/docs/cli/sqoopctl_resolvermap.md b/docs/cli/sqoopctl_resolvermap.md new file mode 100644 index 0000000..c5fbaf2 --- /dev/null +++ b/docs/cli/sqoopctl_resolvermap.md @@ -0,0 +1,32 @@ +--- +title: "sqoopctl resolvermap" +weight: 5 +--- +## sqoopctl resolvermap + + + +### Synopsis + + + +### Options + +``` + -h, --help help for resolvermap +``` + +### Options inherited from parent commands + +``` + -f, --file string file to be read or written to + -i, --interactive interactive mode + -o, --output string output format: (yaml, json, table) +``` + +### SEE ALSO + +* [sqoopctl](../sqoopctl) - Interact with Sqoop's storage API from the command line +* [sqoopctl resolvermap register](../sqoopctl_resolvermap_register) - Register a resolver for a field in your Schema +* [sqoopctl resolvermap reset](../sqoopctl_resolvermap_reset) - reset a resolver map by its name + diff --git a/docs/cli/sqoopctl_resolvermap_register.md b/docs/cli/sqoopctl_resolvermap_register.md new file mode 100644 index 0000000..802a01c --- /dev/null +++ b/docs/cli/sqoopctl_resolvermap_register.md @@ -0,0 +1,41 @@ +--- +title: "sqoopctl resolvermap register" +weight: 5 +--- +## sqoopctl resolvermap register + +Register a resolver for a field in your Schema + +### Synopsis + +Sets the resolver for a field in your schema. TypeName.FieldName will always be resolved using this resolver +Resolvers must be defined in yaml format. +See the documentation at https://sqoop.solo.io/v1/resolver_map/#sqoop.api.v1.Resolver for the API specification for Sqoop Resolvers + +``` +sqoopctl resolvermap register TypeName FieldName -f resolver.yaml [-s schema-name] [flags] +``` + +### Options + +``` + -g, --function string function to use as resolver + -h, --help help for register + -b, --request-template string template to use for the request body + -r, --response-template string template to use for the response body + -s, --schema string name of the schema to connect this resolver to. this is required if more than one schema contains a definition for the type name. + -u, --upstream string upstream where the function lives +``` + +### Options inherited from parent commands + +``` + -f, --file string file to be read or written to + -i, --interactive interactive mode + -o, --output string output format: (yaml, json, table) +``` + +### SEE ALSO + +* [sqoopctl resolvermap](../sqoopctl_resolvermap) - + diff --git a/docs/cli/sqoopctl_resolvermap_reset.md b/docs/cli/sqoopctl_resolvermap_reset.md new file mode 100644 index 0000000..dda7bfb --- /dev/null +++ b/docs/cli/sqoopctl_resolvermap_reset.md @@ -0,0 +1,34 @@ +--- +title: "sqoopctl resolvermap reset" +weight: 5 +--- +## sqoopctl resolvermap reset + +reset a resolver map by its name + +### Synopsis + +reset a resolver map by its name + +``` +sqoopctl resolvermap reset [NAME] [flags] +``` + +### Options + +``` + -h, --help help for reset +``` + +### Options inherited from parent commands + +``` + -f, --file string file to be read or written to + -i, --interactive interactive mode + -o, --output string output format: (yaml, json, table) +``` + +### SEE ALSO + +* [sqoopctl resolvermap](../sqoopctl_resolvermap) - + diff --git a/docs/cli/sqoopctl_schema.md b/docs/cli/sqoopctl_schema.md new file mode 100644 index 0000000..0c4216b --- /dev/null +++ b/docs/cli/sqoopctl_schema.md @@ -0,0 +1,35 @@ +--- +title: "sqoopctl schema" +weight: 5 +--- +## sqoopctl schema + + + +### Synopsis + + + +### Options + +``` + -h, --help help for schema + --name string name of the resource to read or write + -n, --namespace string namespace for reading or writing resources (default "gloo-system") +``` + +### Options inherited from parent commands + +``` + -f, --file string file to be read or written to + -i, --interactive interactive mode + -o, --output string output format: (yaml, json, table) +``` + +### SEE ALSO + +* [sqoopctl](../sqoopctl) - Interact with Sqoop's storage API from the command line +* [sqoopctl schema create](../sqoopctl_schema_create) - upload a schema to Sqoop from a local GraphQL Schema file +* [sqoopctl schema delete](../sqoopctl_schema_delete) - delete a schema by its name +* [sqoopctl schema update](../sqoopctl_schema_update) - upload a schema to Sqoop from a local GraphQL Schema file + diff --git a/docs/cli/sqoopctl_schema_create.md b/docs/cli/sqoopctl_schema_create.md new file mode 100644 index 0000000..df0149e --- /dev/null +++ b/docs/cli/sqoopctl_schema_create.md @@ -0,0 +1,36 @@ +--- +title: "sqoopctl schema create" +weight: 5 +--- +## sqoopctl schema create + +upload a schema to Sqoop from a local GraphQL Schema file + +### Synopsis + +upload a schema to Sqoop from a local GraphQL Schema file + +``` +sqoopctl schema create NAME -f [flags] +``` + +### Options + +``` + -h, --help help for create +``` + +### Options inherited from parent commands + +``` + -f, --file string file to be read or written to + -i, --interactive interactive mode + --name string name of the resource to read or write + -n, --namespace string namespace for reading or writing resources (default "gloo-system") + -o, --output string output format: (yaml, json, table) +``` + +### SEE ALSO + +* [sqoopctl schema](../sqoopctl_schema) - + diff --git a/docs/cli/sqoopctl_schema_delete.md b/docs/cli/sqoopctl_schema_delete.md new file mode 100644 index 0000000..ab10e94 --- /dev/null +++ b/docs/cli/sqoopctl_schema_delete.md @@ -0,0 +1,36 @@ +--- +title: "sqoopctl schema delete" +weight: 5 +--- +## sqoopctl schema delete + +delete a schema by its name + +### Synopsis + +delete a schema by its name + +``` +sqoopctl schema delete [NAME] [flags] +``` + +### Options + +``` + -h, --help help for delete +``` + +### Options inherited from parent commands + +``` + -f, --file string file to be read or written to + -i, --interactive interactive mode + --name string name of the resource to read or write + -n, --namespace string namespace for reading or writing resources (default "gloo-system") + -o, --output string output format: (yaml, json, table) +``` + +### SEE ALSO + +* [sqoopctl schema](../sqoopctl_schema) - + diff --git a/docs/cli/sqoopctl_schema_update.md b/docs/cli/sqoopctl_schema_update.md new file mode 100644 index 0000000..8009919 --- /dev/null +++ b/docs/cli/sqoopctl_schema_update.md @@ -0,0 +1,36 @@ +--- +title: "sqoopctl schema update" +weight: 5 +--- +## sqoopctl schema update + +upload a schema to Sqoop from a local GraphQL Schema file + +### Synopsis + +upload a schema to Sqoop from a local GraphQL Schema file + +``` +sqoopctl schema update NAME -f [flags] +``` + +### Options + +``` + -h, --help help for update +``` + +### Options inherited from parent commands + +``` + -f, --file string file to be read or written to + -i, --interactive interactive mode + --name string name of the resource to read or write + -n, --namespace string namespace for reading or writing resources (default "gloo-system") + -o, --output string output format: (yaml, json, table) +``` + +### SEE ALSO + +* [sqoopctl schema](../sqoopctl_schema) - + diff --git a/docs/cli/sqoopctl_uninstall.md b/docs/cli/sqoopctl_uninstall.md new file mode 100644 index 0000000..12e7546 --- /dev/null +++ b/docs/cli/sqoopctl_uninstall.md @@ -0,0 +1,34 @@ +--- +title: "sqoopctl uninstall" +weight: 5 +--- +## sqoopctl uninstall + +uninstall gloo + +### Synopsis + +uninstall gloo + +``` +sqoopctl uninstall [flags] +``` + +### Options + +``` + -h, --help help for uninstall +``` + +### Options inherited from parent commands + +``` + -f, --file string file to be read or written to + -i, --interactive interactive mode + -o, --output string output format: (yaml, json, table) +``` + +### SEE ALSO + +* [sqoopctl](../sqoopctl) - Interact with Sqoop's storage API from the command line + diff --git a/docs/extra.css b/docs/extra.css deleted file mode 100644 index 3d2c02a..0000000 --- a/docs/extra.css +++ /dev/null @@ -1,3 +0,0 @@ -.md-header { - background-color: #FDC9E5; /* Red */ -} \ No newline at end of file diff --git a/docs/gen_docs.go b/docs/gen_docs.go deleted file mode 100644 index c26595f..0000000 --- a/docs/gen_docs.go +++ /dev/null @@ -1,190 +0,0 @@ -package main - -import ( - "bytes" - "encoding/json" - "flag" - "html/template" - "io/ioutil" - "log" - - "os" - - "strings" - - "github.com/ilackarms/protoc-gen-doc" -) - -func main() { - f := flag.String("f", os.Getenv("GOPATH")+"/src/github.com/solo-io/sqoop/docs/"+"api.json", "input json file") - tmplFile := flag.String("t", os.Getenv("GOPATH")+"/src/github.com/solo-io/sqoop/docs/markdown.tmpl", "template to build from") - outDir := flag.String("o", os.Getenv("GOPATH")+"/src/github.com/solo-io/sqoop/docs/v1/", "output dir") - flag.Parse() - if err := run(*f, *tmplFile, *outDir); err != nil { - log.Fatal(err) - } -} - -func run(file, tmplFile, outDir string) error { - data, err := ioutil.ReadFile(file) - if err != nil { - return err - } - os.MkdirAll(outDir, 0755) - var protoDescriptor gendoc.Template - err = json.Unmarshal(data, &protoDescriptor) - if err != nil { - return err - } - - inputTemplate, err := ioutil.ReadFile(tmplFile) - if err != nil { - return err - } - - fixMapEntryKludge(&protoDescriptor) - getFilesForTypes(&protoDescriptor) - - for _, protoFile := range protoDescriptor.Files { - protoFile.Name = strings.TrimSuffix(protoFile.Name, ".proto") - log.Printf(protoFile.Name) - tmpl, err := template.New("Proto Doc Template").Funcs(map[string]interface{}{ - "p": gendoc.PFilter, - "para": gendoc.ParaFilter, - "nobr": gendoc.NoBrFilter, - "yamlType": yamlType, - "noescape": noEscape, - "linkForType": linkForType, - }).Parse(string(inputTemplate)) - if err != nil { - return err - } - var buf bytes.Buffer - err = tmpl.Execute(&buf, protoFile) - if err != nil { - return err - } - err = ioutil.WriteFile(outDir+"/"+protoFile.Name+".md", buf.Bytes(), 0644) - if err != nil { - return err - } - } - return nil -} - -var filesForTypes = make(map[string]string) - -func getFilesForTypes(protoDescriptor *gendoc.Template) { - for _, protoFile := range protoDescriptor.Files { - for _, message := range protoFile.Messages { - for _, field := range message.Fields { - filesForTypes[field.FullType] = strings.TrimSuffix(protoFile.Name, ".proto") - } - } - } - // overwrite for status and metadata. hacky but the fastest solution! - for _, protoFile := range protoDescriptor.Files { - for _, message := range protoFile.Messages { - filesForTypes[message.FullName] = strings.TrimSuffix(protoFile.Name, ".proto") - } - } -} - -type mapEntry struct { - key *gendoc.MessageField - value *gendoc.MessageField -} - -func fixMapEntryKludge(protoDescriptor *gendoc.Template) { - mapEntriesToFix := make(map[string]mapEntry) - for _, protoFile := range protoDescriptor.Files { - messages := protoFile.Messages - protoFile.Messages = nil - // remove "entry" types, we are converting these back to map - for _, message := range messages { - if strings.HasSuffix(message.Name, "Entry") { - if len(message.Fields) != 2 { - log.Fatalf("bad assumption: %#v is not a map entry, or doesn't have 2 fields", message) - } - mapEntriesToFix[message.Name] = mapEntry{key: message.Fields[0], value: message.Fields[1]} - } else { - protoFile.Messages = append(protoFile.Messages, message) - } - } - } - for _, protoFile := range protoDescriptor.Files { - for _, message := range protoFile.Messages { - for _, field := range message.Fields { - if entry, ok := mapEntriesToFix[field.Type]; ok { - field.Type = "map<" + entry.key.Type + "," + entry.value.Type + ">" - field.FullType = "map<" + entry.key.FullType + "," + entry.value.FullType + ">" - field.LongType = "map<" + entry.key.LongType + "," + entry.value.LongType + ">" - field.Label = "" - log.Printf("changed field %v", field.Name) - } - } - } - } -} - -func yamlType(longType, label string) string { - yamlType := func() string { - if strings.HasPrefix(longType, "map<") { - return longType - } - switch longType { - case "string": - fallthrough - case "uint32": - fallthrough - case "bool": - fallthrough - case "int32": - return longType - case "Status": - return "(read only)" - } - return "{" + longType + "}" - }() - if label == "repeated" { - yamlType = "[" + yamlType + "]" - } - return yamlType -} - -func noEscape(s string) template.HTML { - return template.HTML(s) -} - -func linkForType(longType, fullType string) string { - if !isObjectType(longType) { - return longType //no linking for primitives - } - var link string - switch { - case longType == "google.protobuf.Duration": - link = "https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/duration" - case longType == "google.protobuf.Struct": - link = "https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/struct" - default: - link = filesForTypes[fullType] + ".md#" + fullType - } - return "[" + longType + "](" + link + ")" -} - -func isObjectType(longType string) bool { - if strings.HasPrefix(longType, "map<") { - return false - } - switch longType { - case "string": - fallthrough - case "uint32": - fallthrough - case "bool": - fallthrough - case "int32": - return false - } - return true -} diff --git a/docs/getting_started/docker/1.md b/docs/getting_started/docker/1.md deleted file mode 100644 index d4517fd..0000000 --- a/docs/getting_started/docker/1.md +++ /dev/null @@ -1,263 +0,0 @@ -# Getting started on Docker - -#### What you'll need - - 1. [Docker](https://www.docker.com/) - 1. [Docker-Compose](https://docs.docker.com/compose/) - 1. [sqoopctl](https://github.com/solo-io/sqoop/releases) - 1. [glooctl](https://github.com/solo-io/glooctl/releases) (optional) - - - -### Steps - -#### Deploy Sqoop and Gloo - - sqoopctl install docker sqoop-docker - cd ./sqoop-docker - docker-compose up - -or - - git clone https://github.com/solo-io/sqoop - cd sqoop/install/docker-compose - ./prepare-config-directories - docker-compose up - - -#### Deploy the Pet Store - - docker run -d -p 1234:8080 soloio/petstore-example:latest - -#### Create a Gloo upstream for the petstore - - * using `glooctl`: - -```bash -cat << EOF | glooctl upstream create -f - -name: petstore -type: static -spec: - hosts: - # gateway ip for the docker network - - addr: $(docker inspect sqoop-docker_default -f '{{ (index .IPAM.Config 0).Gateway }}') - port: 1234 -EOF -``` - - - - - * writing directly to disk - -```bash -cat > ./_gloo_config/upstreams/petstore.yaml << EOF -name: petstore -type: static -spec: - hosts: - # gateway ip for the docker network - - addr: $(docker inspect sqoop-docker_default -f '{{ (index .IPAM.Config 0).Gateway }}') - port: 1234 -EOF -``` - - -#### OPTIONAL: View the petstore functions using `glooctl`: - - glooctl upstream get - - +----------+---------+--------+-------------+ - | NAME | TYPE | STATUS | FUNCTION | - +----------+---------+--------+-------------+ - | petstore | static | | addPet | - | | | | deletePet | - | | | | findPetById | - | | | | findPets | - +----------+---------+--------+-------------+ - -The upstream we want to see is `petstore`. The functions `addPet`, `deletePet`, `findPetById`, and `findPets` -will become the resolvers for our GraphQL schema. - - -#### Create a GraphQL Schema - -Copy and paste the following schema into `petstore.graphql` (or wherever you like): - -```graphql -# The query type, represents all of the entry points into our object graph -type Query { - pets: [Pet] - pet(id: Int!): Pet -} - -type Mutation { - addPet(pet: InputPet!): Pet -} - -type Pet{ - id: ID! - name: String! - status: Status! -} - -input InputPet{ - id: ID! - name: String! - tag: String -} - -enum Status { - pending - available -} -``` - -#### Upload the Schema - -Upload the schema to Sqoop using `sqoopctl`: - -```bash -sqoopctl schema create petstore -f petstore.graphql -``` - - -#### OPTIONAL: View the Generated Resolvers - -A Sqoop [**ResolverMap**](https://sqoop.solo.io/v1/resolver_map/) will have been generated -for the new schema. - -Take a look at its structure: - -```bash -sqoopctl resolvermap get petstore-resolvers -o yaml - -metadata: - resource_version: "1" -name: petstore-resolvers -status: - state: Accepted -types: - Mutation: - fields: - addPet: {} - Pet: - fields: - id: {} - name: {} - status: {} - Query: - fields: - pet: {} - pets: {} -``` - -The empty `{}`'s are Sqoop [**Resolver**](https://sqoop.solo.io/v1/resolver_map/#sqoop.api.v1.Resolver) -objects, waiting to be filled in. Sqoop supports a variety of Resolver types (and supports extension to its -resolution system). In this tutorial, we will create Gloo resolvers, which allow you to connect schema fields -to REST APIs, serverless functions and other Gloo functions. - -#### Register some Resolvers - -Let's use `sqoopctl` to register some resolvers. - -```bash -# register findPetById for Query.pets (specifying no arguments) -sqoopctl resolvermap register -u petstore -f findPetById Query pets -# register a resolver for Query.pet -sqoopctl resolvermap register -u petstore -f findPetById Query pet -# register a resolver for Mutation.addPet -# the request template tells Sqoop to use the Variable "pet" as an argument -sqoopctl resolvermap register -u petstore -f addPet Mutation addPet --request-template '{{ marshal (index .Args "pet") }}' -``` - -*Note*: if you get a `permission denied` error, run -```bash -sudo chown -R $USER _gloo_config -sudo chgrp -R $USER _gloo_config -``` - -That's it! Now we should have a functioning GraphQL frontend for our REST service. - -#### Visit the Playground - -Visit the Sqoop UI from your browser: http://localhost:9090/ - -You should see a landing page for Sqoop which contains a link to the GraphQL Playground for our -Pet Store. Visit it and try out some queries! - -examples: - -```graphql -{ - pet(id:1 ) { - name - } -} -``` - -↓ - -```json -{ - "data": { - "pet": { - "name": "Dog" - } - } -} -``` - -```graphql -{ - pets { - name - } -} -``` - -↓ - -```json -{ - "data": { - "pets": [ - { - "name": "Dog" - }, - { - "name": "Cat" - } - ] - } -} -``` -```graphql -mutation($pet: InputPet!) { - addPet(pet: $pet) { - id - name - } -} -``` -with input variable -````json -{ - "pet":{ - "id":3, - "name": "monkey" - } -} -```` - -↓ - -```json -{ - "data": { - "addPet": { - "name": "monkey" - } - } -} -``` diff --git a/docs/getting_started/kubernetes/1.md b/docs/getting_started/kubernetes/1.md deleted file mode 100644 index 6f8d1e8..0000000 --- a/docs/getting_started/kubernetes/1.md +++ /dev/null @@ -1,226 +0,0 @@ -# Getting Started on Kubernetes - -### What you'll need -- [`kubectl`](https://kubernetes.io/docs/tasks/tools/install-kubectl/) -- [`sqoopctl`](https://github.com/solo-io/sqoop) -- [`glooctl`](https://github.com/solo-io/gloo): (OPTIONAL) to see how Sqoop is interacting with the underlying system -- Kubernetes v1.8+ deployed somewhere. [Minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/) is a great way to get a cluster up quickly. - - - -### Steps - -#### Deploy Sqoop and Gloo - - sqoopctl install kube - - -#### Deploy the Pet Store - - kubectl apply \ - -f https://raw.githubusercontent.com/solo-io/gloo/master/example/petstore/petstore.yaml - -#### OPTIONAL: View the petstore functions using `glooctl`: - - glooctl upstream get - - +--------------------------------+------------+----------+-------------+ - | NAME | TYPE | STATUS | FUNCTION | - +--------------------------------+------------+----------+-------------+ - | default-petstore-8080 | kubernetes | Accepted | addPet | - | | | | deletePet | - | | | | findPetById | - | | | | findPets | - | gloo-system-control-plane-8081 | kubernetes | Accepted | | - | gloo-system-ingress-8080 | kubernetes | Accepted | | - | gloo-system-ingress-8443 | kubernetes | Accepted | | - +--------------------------------+------------+----------+-------------+ - -The upstream we want to see is `default-petstore-8080`. The functions `addPet`, `deletePet`, `findPetById`, and `findPets` -will become the resolvers for our GraphQL schema. - - -#### Create a GraphQL Schema - -Copy and paste the following schema into `petstore.graphql` (or wherever you like): - -```graphql -# The query type, represents all of the entry points into our object graph -type Query { - pets: [Pet] - pet(id: Int!): Pet -} - -type Mutation { - addPet(pet: InputPet!): Pet -} - -type Pet{ - id: ID! - name: String! - status: Status! -} - -input InputPet{ - id: ID! - name: String! - tag: String -} - -enum Status { - pending - available -} -``` - -#### Upload the Schema - -Upload the schema to Sqoop using `sqoopctl`: - -```bash -sqoopctl schema create petstore -f petstore.graphql -``` - - -#### OPTIONAL: View the Generated Resolvers - -A Sqoop [**ResolverMap**](https://sqoop.solo.io/v1/resolver_map/) will have been generated -for the new schema. - -Take a look at its structure: - -```bash -sqoopctl resolvermap get petstore-resolvers -o yaml - -metadata: - namespace: gloo-system - resource_version: "573676" -name: petstore-resolvers -status: - state: Accepted -types: - Mutation: - fields: - addPet: {} - Pet: - fields: - id: {} - name: {} - status: {} - Query: - fields: - pet: {} - pets: {} -``` - -The empty `{}`'s are Sqoop [**Resolver**](https://sqoop.solo.io/v1/resolver_map/#sqoop.api.v1.Resolver) -objects, waiting to be filled in. Sqoop supports a variety of Resolver types (and supports extension to its -resolution system). In this tutorial, we will create Gloo resolvers, which allow you to connect schema fields -to REST APIs, serverless functions and other Gloo functions. - -#### Register some Resolvers - -Let's use `sqoopctl` to register some resolvers. - -```bash -# register findPetById for Query.pets (specifying no arguments) -sqoopctl resolvermap register -u default-petstore-8080 -f findPetById Query pets -# register a resolver for Query.pet -sqoopctl resolvermap register -u default-petstore-8080 -f findPetById Query pet -# register a resolver for Mutation.addPet -# the request template tells Sqoop to use the Variable "pet" as an argument -sqoopctl resolvermap register -u default-petstore-8080 -f addPet Mutation addPet --request-template '{{ marshal (index .Args "pet") }}' -``` - -That's it! Now we should have a functioning GraphQL frontend for our REST service. - -#### Visit the Playground - -Visit the exposed address of the `sqoop` service in your browser. - -If you're running in minkube, you can get this address with the command - -```bash -echo http://$(minikube ip):$(kubectl get svc sqoop -n gloo-system -o 'jsonpath={.spec.ports[?(@.name=="http")].nodePort}') - -http://192.168.39.47:30935/ -``` - -You should see a landing page for Sqoop which contains a link to the GraphQL Playground for our -Pet Store. Visit it and try out some queries! - -examples: - -```graphql -{ - pet(id:1 ) { - name - } -} -``` - -↓ - -```json -{ - "data": { - "pet": { - "name": "Dog" - } - } -} -``` - -```graphql -{ - pets { - name - } -} -``` - -↓ - -```json -{ - "data": { - "pets": [ - { - "name": "Dog" - }, - { - "name": "Cat" - } - ] - } -} -``` -```graphql -mutation($pet: InputPet!) { - addPet(pet: $pet) { - id - name - } -} -``` -with input variable -````json -{ - "pet":{ - "id":3, - "name": "monkey" - } -} -```` - -↓ - -```json -{ - "data": { - "addPet": { - "name": "monkey" - } - } -} -``` diff --git a/docs/img/Sqoop.png b/docs/img/Sqoop.png deleted file mode 100644 index 10b635782c022a630f443ef913a56daa1388e5bd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 62883 zcmXt91yB^w7F|R@>5wh~1x4v@X%RuXQ@Xnwq+6sLq`SMNq(yS+?(Y0={r@w=FvIM= zxN+{e@q%Tf#nF(7kRcEVn#4yDIS2%P7W@W0Lj?c9QQsN>f1X=^RJDUZ=&4}8aLzdb zju6O8h=hosg7fU&yob4hQ5wV5VS(Mw5Q7Y2vJPs6i;1<+xD7Youo&NS!@67aD&@ok z!Y^g|Qi}u*?^^!sE9z9!?SH&vALa8Z{@Vyocm646u3ub
KL0^1c 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\Ml9%7Q&YnZ?hxviXZg{l`7vWfLA1!(W{GBYHBF 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

k2hFy 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�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/tcp docker-compose_sqoop_1 -90645fff651e soloio/control-plane:0.4.4 "/control-plane --st…" 31 hours ago Up 5 seconds 0.0.0.0:8081->8081/tcp docker-compose_control-plane_1 -36bb7f23808c soloio/envoy:0.4.4 "envoy -c /config/en…" 31 hours ago Up 6 seconds 0.0.0.0:8080->8080/tcp, 0.0.0.0:19000->19000/tcp docker-compose_proxy_1 -7747741da1df soloio/function-discovery:0.4.4 "/function-discovery…" 31 hours ago Up 6 seconds docker-compose_function-discovery_1``` -``` - -Everything should be up and running. If this process does not work, please [open an issue](https://github.com/solo-io/gloo/issues/new). We are happy to answer questions on our diligently staffed [Slack channel](https://slack.solo.io) - -See [Getting Started on Docker](../getting_started/docker/1.md) to get started creating your first GraphQL endpoint with Sqoop. \ No newline at end of file diff --git a/docs/installation/kubernetes.md b/docs/installation/kubernetes.md deleted file mode 100644 index c4aef9a..0000000 --- a/docs/installation/kubernetes.md +++ /dev/null @@ -1,72 +0,0 @@ -# Installing on Kubernetes - -## Installing with `kubectl` - -#### What you'll need - -1. Kubernetes v1.8+ or higher deployed. We recommend using [minikube](https://kubernetes.io/docs/getting-started-guides/minikube/) to get a demo cluster up quickly. -1. [`kubectl`](https://kubernetes.io/docs/tasks/tools/install-kubectl/) installed on your local machine. - -Once your Kubernetes cluster is up and running, run the following command to deploy Sqoop and Gloo to the `gloo-system` namespace: - -```bash -# install Sqoop -kubectl apply -f \ - https://raw.githubusercontent.com/solo-io/sqoop/master/install/kube/install.yaml -``` - - -## Installing with `sqoopctl` - -#### What you'll need - -1. Kubernetes v1.8+ or higher deployed. We recommend using [minikube](https://kubernetes.io/docs/getting-started-guides/minikube/) to get a demo cluster up quickly. -1. [`kubectl`](https://kubernetes.io/docs/tasks/tools/install-kubectl/) installed on your local machine. -1. [`sqoopctl`](https://github.com/solo-io/sqoop/releases/) installed on your local machine. - -Once your Kubernetes cluster is up and running, run the following command to deploy Sqoop and Gloo to the `gloo-system` namespace: - -```bash -sqoopctl install kube -``` - -## Confirming the installation - -Check that the Gloo pods and services have been created: - -```bash -kubectl get all -n gloo-system -NAME READY STATUS RESTARTS AGE -pod/control-plane-757bd75db7-9vw59 1/1 Running 0 2h -pod/function-discovery-7df6bd4fcd-26rx8 1/1 Running 0 2h -pod/ingress-77c7bd6577-kdgdd 1/1 Running 0 2h -pod/kube-ingress-controller-78bfc4c84d-8tvjr 1/1 Running 0 2h -pod/sqoop-6b79fdc655-n7j6n 2/2 Running 0 2h -pod/upstream-discovery-59bc6f7889-g65z6 1/1 Running 0 2h - -NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE -service/control-plane ClusterIP 10.96.24.217 8081/TCP 3h -service/ingress LoadBalancer 10.111.152.102 8080:31972/TCP,8443:30576/TCP 3h -service/sqoop LoadBalancer 10.106.92.208 9090:31470/TCP 3h - -NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE -deployment.apps/control-plane 1 1 1 1 3h -deployment.apps/function-discovery 1 1 1 1 3h -deployment.apps/ingress 1 1 1 1 3h -deployment.apps/kube-ingress-controller 1 1 1 1 3h -deployment.apps/sqoop 1 1 1 1 3h -deployment.apps/upstream-discovery 1 1 1 1 3h - -NAME DESIRED CURRENT READY AGE -replicaset.apps/control-plane-757bd75db7 1 1 1 3h -replicaset.apps/function-discovery-7df6bd4fcd 1 1 1 3h -replicaset.apps/ingress-77c7bd6577 1 1 1 3h -replicaset.apps/kube-ingress-controller-78bfc4c84d 1 1 1 3h -replicaset.apps/sqoop-6b79fdc655 1 1 1 3h -replicaset.apps/upstream-discovery-59bc6f7889 1 1 1 3h -``` - -Everything should be up and running. If this process does not work, please [open an issue](https://github.com/solo-io/sqoop/issues/new). We are happy to answer -questions on our [diligently staffed Slack channel](https://slack.solo.io/). - -See [Getting Started on Kubernetes](../getting_started/kubernetes/1.md) to get started creating your first GraphQL endpoint with Sqoop. diff --git a/docs/introduction/architecture.md b/docs/introduction/architecture.md deleted file mode 100644 index 738c22d..0000000 --- a/docs/introduction/architecture.md +++ /dev/null @@ -1,25 +0,0 @@ -# Architecture - -The Architecture of Sqoop can be understood as follows: - -![Architecture](low_level_arch.png "Sqoop Architecture") - - -Sqoop users interact via the [Storage Layer API](https://github.com/solo-io/sqoop/tree/master/pkg/storage). - -Declarative API Objects ([Schemas](../v1/schema.md) and [ResolverMaps](../v1/resolver_map.md)) are -written by the User (usually via `sqoopctl`, the Sqoop CLI) and polled by Sqoop. - -When Sqoop detects an update to an API Object, it re-syncs its state to match -the user specified configuration. - -Sqoop is composed of two components: a GraphQL service and an Envoy Proxy functioning as a sidecar. Rather than manually configuring -its own sidecar, Sqoop directs Envoy to connect to Gloo as its [control plane](https://github.com/envoyproxy/data-plane-api/blob/master/XDS_PROTOCOL.md), -allowing Sqoop to leverage [Gloo's large set of HTTP routing features](https://gloo.solo.io/#features). - -Sqoop generates [Gloo config objects](https://gloo.solo.io/v1/virtualservice/) in a self-service fashion, allowing Gloo -to handle service discovery, [Gloo plugin configuration](https://gloo.solo.io/plugins/aws/), and configuration of -[Envoy HTTP Filters](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/http_filters.html) - -Once Gloo has applied the desired configuration to Envoy, Sqoop begins listening for incoming GraphQL requests, serving queries -against the schema(s) provided by the user(s), and making requests via Envoy based on the configuration in the user-defined [ResolverMaps](../v1/resolver_map.md) diff --git a/docs/introduction/concepts/api_objects.md b/docs/introduction/concepts/api_objects.md deleted file mode 100644 index fe3007d..0000000 --- a/docs/introduction/concepts/api_objects.md +++ /dev/null @@ -1,85 +0,0 @@ -# ResolverMaps - -### Storage-Based API - -Sqoop, like [Gloo](https://gloo.solo.io), features a storage-based API. Inspired by Kubernetes, Sqoop's API is accessed -by applications and users by reading and writing API objects to a storage layer Sqoop is configured (at boot-time) to monitor -for changes. Currently supported storage backends are [Kubernetes CRDs](https://kubernetes.io/docs/tasks/access-kubernetes-api/extend-api-custom-resource-definitions/), -[Consul Key-Value Pairs](https://www.consul.io/), or Sqoop's local filesystem. - - -### API Objects - -Sqoop's API Objects take two forms: - -0. [Schemas](../../v1/schema.md) - * Schemas are made up of three pieces of information: - - A name for the schema. This can be anything, but must be uniquee - - An inline string containing the entire [GraphQL Schema](https://graphql.org/learn/schema/) - - The name of a ResolverMap object which contains Sqoop-specific instructions - on how to resolve the fields of the schema. - - If the user leaves this empty, - Sqoop will attempt to generate an empty ResolverMap skeleton for the user, - which the user can edit using `sqoopctl`. - - * GraphQL Schemas can be uploaded to Sqoop using `sqoopctl` - -1. [ResolverMaps](../../v1/resolver_map.md) - * ResolverMaps represent a mapping between the fields in a GraphQL schema - and the [Resolvers](resolvers.md) that Sqoop will use to resolve them. - - * Resolvers define the action Sqoop will perform when executing a GraphQL Query. Sqoop leverages - [Gloo's function registry](https://gloo.solo.io/introduction/concepts/#Functions) to generate resolvers, - allowing users to define GraphQL resolvers using configuration rather than code. - Read more about GraphQL Resolvers here: https://graphql.org/learn/execution/ - - * An example ResolverMap might look like the following: - - name: starwars-resolvers - types: - Droid: - fields: - appearsIn: - template_resolver: - inline_template: '{{ index .Parent "appears_in" }}}' - friends: - gloo_resolver: - request_template: '{{ marshal (index .Parent "friend_ids") }}' - single_function: - function: GetCharacters - upstream: starwars-rest - Human: - fields: - appearsIn: - template_resolver: - inline_template: '{{ index .Parent "appears_in" }}}' - friends: - gloo_resolver: - request_template: '{{ marshal (index .Parent "friend_ids") }}' - single_function: - function: GetCharacters - upstream: starwars-rest - Query: - fields: - droid: - gloo_resolver: - request_template: '{"id": {{ index .Args "id" }}}' - single_function: - function: GetCharacter - upstream: starwars-rest - hero: - gloo_resolver: - single_function: - function: GetHero - upstream: starwars-rest - human: - gloo_resolver: - request_template: '{"id": {{ index .Args "id" }}}' - single_function: - function: GetCharacter - upstream: starwars-rest - - Here we have defined resolvers for the fields `Query.droid`, - `Query.hero`, `Query.human`, `Human.friends`, `Human.appearsIn`, - `Droid.friends`, and `Droid.appearsIn`. - diff --git a/docs/introduction/concepts/resolvers.md b/docs/introduction/concepts/resolvers.md deleted file mode 100644 index 8b6ce39..0000000 --- a/docs/introduction/concepts/resolvers.md +++ /dev/null @@ -1,65 +0,0 @@ -# Gloo Resolvers - -Gloo resolvers are the primary means of resolving schema fields using Sqoop. This -document explains the structure of a Gloo resolver and how to write one. - -## Request Templates - -Let's take a look at the structure of a Gloo resolver (usually written by users as YAML, -stored by Sqoop as Proto). - -```yaml -gloo_resolver: - request_template: '{{ marshal (index .Parent "pet_ids") }}' - response_template: '{{ "success" }}' - single_function: - upstream: petstore - function: ListPets -``` - -* `request_template` is optional. If provided, Sqoop will use the provided template -to construct the request body sent to the resolver function. - -Request templates follow the conventions of [Go templates](https://golang.org/pkg/text/template/). - -Available parameters for use in Request Templates come from the -[`Params`](https://github.com/solo-io/sqoop/blob/master/pkg/exec/executable_resolvers.go) object. - -The Params Object has the following structure (defined in Go): - -```go -type Params struct { - Args map[string]interface{} - Parent map[string]interface{} -} -``` - -`Args` represent arguments that were passed to Sqoop as part of the Client Query. - -`Parent` represents the root object the field under query belongs to. `Parent` -is `nil` for root types (`Query` and `Mutation` type). - -The `marshal` function is available for use in Sqoop templates. -`marshal` will encode any value into JSON. - -Here's an example of a Gloo Resolver using multiple destinations, with load balancing: - -```yaml -gloo_resolver: - request_template: '{{ marshal (index .Parent "friend_ids") }}' - response_template: '{{ marshal (index .Parent "friend_ids") }}' - multi_function: - weighted_functions: - - weight: 1 - function: - upstream: petstore-v1 - function: ListPets - - weight: 1 - function: - upstream: petstore-v2 - function: ListPets -``` - -## Response Templates -Response templates also use Go template syntax. Response templates can refer to -(sub)fields of the response body, provided that it is JSON-encoded. \ No newline at end of file diff --git a/docs/introduction/high_level_architecture.png b/docs/introduction/high_level_architecture.png deleted file mode 100644 index 7782c7724dbcee795802630d39dd276930e0434d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 423757 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

X|E?$_B4{2 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≤stzY8ajj(l!$daenk~`6agvcil+hpZ~ehGzB6!U`dsdKsNZmgQcOawAq8n#9CBN(5j76@<3g%MSDGjItF(yWpFksS;Ra_d^$Zp-#bKF zVr64fv?~=$cA)CehEpm;g0)3$4FIhnPZ`oICp<)fru|?idG4V->k8`7TB~g@6MHvu zk|5OTL(_!xIgp6s=YT&s9*B$>ONM&Z^$=||*6SsDI4Q~y4h~K`OTHe4?#lYx!mpgw zs;Ag2BIu1u9dx*0NMXY&5S5+p6}yG3Ekl~?7HOL?yH$$tcQkveo~3XP5e=r&rv5GZ ztPs3SUU%rXgI||E{dREC7QH3k-Mpo;@AsjT*wPuZ^PvNEV4eYE>eEir{QQa*OY!dS zdFtAH5GRM1@S?9`q8V`KC&_sSJgkcP-i~bWN_uuMgyaMo$H-%jKsc3sac>0=k7R!f$Nhf*HX8c_5B6$rzk0CECmu$u5y;bui-yF0pgMsKvP9N`oIt(on*ExzpU16XkA z+!q0vfQTjJv;`XCjY8iaQiHn*mzHSmsa7M;xCjNO{GA;{aU<^XYG(!%03c+T8Pcs} z#?`ld-6yF<`K@FWhF`)f9<-C?Up$v2!zCyXk!KBzKvwZpA3_s-aFx0Dado>3G@F8#VqmP`*3_i3QqpW+CR;Byzn>pw(uM-VvjB$o%o^Yk>qd`i8)ZIW~Z&f zv$$mo(&L5-|9h$O>Cxd*Cv0kg?iJSP{Pp(fEo4Rm)wd)_EYOx@QM5>uL%IW*_*9!>bQ+2LKb_4C)Z^RBswI$awR*g`WkBTRNV{vs>dj z)XC8rJ#j8i9~=jxCyfC|h99uMB@HwqQ6V>D?f13ApP}Iu3wmATeGaGo7ENtG&{qr| zEEcKC8GhmE61s3fP)l;=)}r?6lZwRu)IvO|{UIU2xO-0iJPFmY#q@oaLYFP=iW|v# zhOZk*%x{-VbBFJdz10x8R<{pWQ@-Z@90Hi<+)udn6t=7C%5}!@|+VsXBKZz{z zGvqH& zR3Q1!20-#gvS=oWGM~;0dRpfmT5}v;U|OtRd*aUS{qTnS(xUGn92LzJ3Aze158by1 z&FX#G55F3Qmrba;!smZsieQW2h$PRHez1dp_V5A?9u%jpol}Cjcm2xgNN(W6!^0{S zMeyI=H)ybL{ysKaQ=$@W>x!s*X!;QWSO3#X;Hk+Tbp8FXQ$X_dto@Hy=UqL=UWi|m9L-Z zX+|C9EXN3-w22#UTg6cC7oDv&{!R+C|3~~E6<^C^&@XCG8*-T|J^@9=Ub5-56hPWg zHX=-jBpLZCk<}oxWyqk?^f;H6nnH-vfN5iIPpm<7?h1<4o`x#Qi=Qh%8DbC`aGOjZ z#y4a{f)VU^`@MzF@Csz#iy%kt1kEC>QGZtrvA*7EX*Qh5vqr7oa&8pTniR%HgkPbt zbpmrdZ)Pc9-}eIir36ev?hE+L z1#AB~-PPk#7;xR*f9iZ9OJQ#i#`bFHaS8AL8z#v96D^GVj92IL)2oG+;noLx5o2jX zwBMp;UhO5Tk}Yu)xOD_C!&^|$_E1HHcvRCf7WOf3@!^pw2>5Zg)*GEQcswHgce$bp zZ#owO4A(O=N|m|ke|*ssocS@i4zhEp=YjaP-Db^|nqg#Qy*uAHZ>a1!2+ZD%7=a1- z@Rtv7c?1Lm>W8n__x3n&a8_so&s1K`Zu}pC=Bvr6{S|+g?zBPT z8bvrZnq`Yd`~4F&gslxJ=G=|~B&~`(sXCk@Y5~!>Op3yC{~cdm=Zw%dqZ&uiq#N7x z3b_fYQgX7qKMs@k+5aby{qMsBjL;YfY6>E}HGluitx|oGJ95acrl`(@pRO8tO!apJ z-Mk9jLy0O_T-#^-G^;-2+XwFS_CXazmS~t9R6?HXSrQ2H3;43l@lh?RDOHi?hvWJ= z)Bk@X07bOGgM9VCUj%2C168cSmxiIYx&xd6a1PFLsGs?|F55A~gJ#_6HERS14Tz#0 zE9AfZd}|y`F)tc(M;H0H2ubOE2MVx=i&?E#_rkKhLx&e=XCm)z53QvYR0ZSW;3wmN zYf%Ms7U&d_{9Xzqiz!+82qyQvQ~-F4cK2~+fPdJ0#bocbUr@1Jm9uzw>92W30n&TF z6aT9&@$1?;OCC1~?Ox77$If=G>zrthLrRPOe1PFK{^Oyfl$^ntj%pE=?JfOY4n~d1 z(HW;))+Y$l&*0tb z#hRg@$pL%l({P%vQFbV1$*vGkUMF?ha-9M!=x>fiI5jJON=ipb4%Sp<(??IIXpu36 z#1T>9fwJsh7kYBk#s=DM**@khv2*$#&SyFh;ZSw1TKW9=Q^NRty}^CWn`ZN0II)aJ zzRFh~r-dwsB-(z+fRB(ZtyivKo-QArJ5^0ad}?dU4UqQFO>qOZG3X2+Y?nZu1|N=S zi~%=g-##b*hOX;mdOh>zAfBX2Q`q)lQ}o}dcW)ZHP*#)P4pw~bA@y;CpkK`C!)|Ea zYwUYjRA0NY;CIh;wHNI=b@+xKpOdibTF(t$nXMyC@X)GHWOPQ>q=97gN7*vwiZy)` zBRxWe6%^DcYR?a-+CNZGD2zA@qecX^aL)IZcc5Twz8{97E@;iKD7*qgRHZx1TOuOS z%lDW)nkqC+y%_mmcp6WSP>T?H1e*$g)eCK4TRE(po&oCJe;S+6;VBu zpy@{KO7T{!OEf+z(MjUY&85+F#a%u$aQq3dYWzD{0jo&~B2GST`^WpQ*R)_r=Z*nu zF%K5)SW}@sx;o{m)tED)#o@bP{XOQ|QO{ z6Ti-prXslpFPTr5ZV|%GM5KlHp*mk0yQ)H8df&=Ax8)Hd`q7xOlHVQKp7rq2eZ+!T z>Ms)fbX*Xi6I44K2wiy0DIzML_dBj}nL%;XQ)95xU8I$HOY=@C7>Ug^}D^!teTC=$jIR4>?BS0yffg?Nc8WDxje(oKH-A~ zo=dM(h`V<4)8&Rhz{Rcy@s2)CRm-)_SW}|SxH>7p_pxpyC5Pft%!lFiwfy09Fnh0o zH1OI+@qf$yStt03wTAL|Gd(2}SwT@H$M)kQYO=%TT~+a?@9)HV7b`Ast-!*0R{>AO zEoKZ_pP0bFIb$@dhsa2)lUI+xwB8%&X|(Ac?VooartYOyw)y#)@}TSEg>dJ~IQpiX zgC`qRUqo3N_;^y-8TGfGjbK=(W;)%k4`KtVNVOU7hh?j7HZtOJPd}K{anX^ekMyN~ zCQ~O!_EvL8Srn6Ux9uh56kL7*)T<>lg{9NFXz-e7cA5yEw7j?6Giz^YXiMO=`hy&{ ziX(&J|waE6OH!*=#`ucR#@kB*6|4i)gTaVjwKD0{lR1+k`#)S&L5P7kzBm=25 z4zpo`23>T^Y1qtlPQCnQRG~n8FW9D3Y@XY80me)VJG6DE@-Cj4MMX>|dGVIo+;32A#eEOPEEid4=y)8a6pH5Hj z78qXoM{OSWBl)U54=hD@H!-51a`|BjbR0E;TKaNXNf%sN;;g)ejVOT*%O$Y+^~Kit zYrU**9d+Pdju@mHy$XTR4uEq2{%KPJ$fGpQ+@}GTk&JfZ(&u`lUBze!lNjLzOd^cH z$Q|2a_%ejTb}h+2*g{z4iyaDKx)N}&`QlerbtTL(2%IoC6-f50)m#pg=a1fV_Zbx& zP5E5j2L$bdO}a~8{zIZ#{1~)7wHeiOoaDs@7)a6?SRZ`NP@I zJnqnG-pJI&a|B|HeSrE&ErmcI{mQ(EkAtyi0C`kH^NRSfyAxxymX_6Zw=q{3C!A#d zGQ}DAHs{bwj|b~|tv=Mu9@a?Ed*6}O5soY>;|nW&5panFkg_+wKmWSCU)drAK$)sV zn0vj(T+paH)ROIL!jQHpi##rzVP2=|ZyYE%F0h5tKTcpNznUe`eWY_W zwn~LqAg+CH*|Ba3F9rm5$;e!GU`RMk+N^~(F+|ivvd1IJt?Z3z3d=rBd#2LQ*wHxs zpV`wZ`nP;qR?#fK-ER-}k<;Hm4LdL_4DIG6d|%sA14T?~erLp)ArKypiO<8&PfJ5V zA*91?M1@@v7FhF{;3IdjC0pRxZ%mcuG;k>G1E4<)5M4c!+Fdq#^aGZ0)MIh3EDa8? z?`0fG)2X_%6#kY`fLArr2%CR>V-o zkhyy_{ixdw%x!J;>&AV6OXyjrfR^_oOtJYpY8nL=Gzsfi+EMv#gL2|1>gCh4$vDs| zmP0+R@djG1K#6dH*4Q^fdxmJc5}qlUR*76wFWe0fCiVf~1ajDeOW7oSt9hffDm25) zry}+AWTfU^w+N(J>HQ&(ko6CSh0KoJk zF|g311~6${MMsx>R>Bm`ym#=8=d8ZZgWBxnI@kA+&H-MmM4yf-I(+wNG^7wGP@S`Q zyH5%;MBi;Y2cfN4qk1OOAncFX3aE0hOTKV6bF})j#F1vB(KW{s+z<_mGpww>-DSP&*%DaMCmx3J`n7K$i@(lP|*0)*G?DiFj~HBOLfI2~lq0T7ti zYbFTAoK^kovrDg6%8uK)_uMxo+NJltGF3gSF1H_hE+s<*+WxQ)p)m%3I_*35Al(=Y zyx$X8HW4V2V+dM#qukAO7!QfA&#zFiJ%AAgIX=Q0O!pBgwFxRrzfuPY(@WOi3Im|7 zZ52OS;w|xzKO0n$DB=vB!-ZcD=4*Lzbp}Hz$SBAhp8+9-OA2V+K)bO^KRI(=xDH~# zYsM^~4Peys2Gils&m)~S5u2;1e%>PJm=$n2^L*Df2neOU24fqHO{s|ZUb#%(0!#-y zTu2npu-^lGkXA^9XS{)c$!5TKRtaYo`XRianUitBs4Zc7Pv2qnt0L7)PiN=mORj^z zDt1pg#XiEda{*(@R}oCFS$4=~xxOrmaP~T#(T&fr4Rp3T)>|4(DJb2Yg*d4mF-_xK zPVs*#+d;Jj4b(%?*54m=?3VHEaD=}Zw7Bn$D-T#MdM^f?M`pT~Pw+=Enmmh3PXFx8 z^bZG^`~r$epf}9^wOz4>*UT?Y{7k2{ZsBHOK@iv1&cXt`Xzu+EW$z(8sfGCPZ%P5` zhtm#L4cqbf9%rB_1m&h>vgk8r&x~vg5MM9~v`?r2R5Vg%YufyzvF>X*eb%?SDH&3q zq_0EA13%=G%!XGxiRXF$n+0elr>QXt@2m|u{5C6yGkTxo@@D%r;J)FL52tA>lJ1D$ z|ImIcIqDg296J+yij(;Y7-Q)>-Wofl5_DV+s{2`6Rpr94CRKwZMk0RpsMoGckW~LM zrZD~V-`F14F7bnofQ5i1r*{srPJJB#zxh#X2p91QTV+#(0Bcg)(&_Z!m_bI=ha+)Q ztF_HkRiw4}!*A*f=2bf(pilt@L}YL@1W2h#2D-qSH}2R#pOq-X6)Z zSlva1r-567ZoBePS=D;ezI~u&Aiy52bkyQ0A}IsSjBRV6Qg!@m z3!sjG%Z9#2Q2@<|tvMq?X&-=Q5*2Y^cBW1uY7`zzi95q`}5H*2FRa} zSi|Dt*bfZ%>&WOH_ME=`3y$|9MA1Oj2%9k{h(6qjc$~&->c?3a7TVlwBH`q^$7T=8;;J;&fPvUM8z$-*0~;@0;ILBhD^NW@V$Z;e z+U7c}&&Oo1AM8p{R6^qbo1bZ+$wg4r5aj>ESOUW_li-TKaeriefaC!@S~3pJb4Hy? zMzB#}X5jn(J2a4m?~M9tmHsA&y;3n zIz2Sv;U^R@W1>$Bbez);ixA&e57;q6*>`_={G|LzVx?Yr{db^q|2ncgfVS`SS}hcy z)Ah3X8&76({@7qUj;l`#-sd;>o2su?y|J6a+fK>p=Sv@}(Y^mx3rlqP9k59(dTw!2 z#d%2$A-7^7m%SG_OFI(nBuc*_z86n!6Tuqt6t&acnR8{o3(C-As1P+C;>m}1!*p@j zvwP?#0{;&A574mTdTd068$iW*#U1crUbx-%gWHa8%ppHAIP_*MUBc`=;+e&^4S6ba9# z$d=ZgP$8#cMn%)LlYZ%`duK=&lEE1$ZUibs9(}Wz9RQd5k`&@h&^k%6Ggr}~A;a0dC1w>8QHETIVxq2)z125!Sst57*m6Nq3#;@sX@>S?frO0D!2w< z?ktl+9Zr4B{$e^;sZK)oUXexE090jj7~A&o2=8M7`cyEIfUc1JSD_2^g)%o5@88d> z26cspUY?82hmXpc`UKiMY(zcto&WJ>cMd!2*F{=H8C(n_I5=G0eY&EBGoAKKBi-xz z;yQrdLu{~F=q26Kv zX}H74jm{xHCr}D0N-^g%;m?YfQNST^M;5^AvKg_br93OdNybC}*{;P81We8vdn`r8 zf-sq0@)oFb3OyIW0tfxo$h^I+do?lM@}ac<+-&@SFC)n!En#btOwE9<`6KsG&Ds(I zN?#Xycl-C%#56!E1HpR=_$s|^zW=_(ZWWfmWczkR?oq7t%v%uLDs3pyc8!XPYG$gU z1$10QN=u6B&gsMtXu+Q<^)W!ca)1KY>2Qr{`#@tF-nMyL52`Ou5a|B@qv|c7qWZ$V z(V@E}r9(=jq(vA~krWhZDJe;%bA~Pn=`IOrkj|k&xXX@BPkNtThW* zXYZ$fPt4w&Ho)K#0^0%af78WhnnR$aJWiSWRE6v+-wp@^Z9mb7*=Dd~gMYxrG}YYG z3H<9{z*l>LS>p0;Du|U}Pi98=%EJjg9sOW$P!LfMl_Y4P1slFthle4lU~?@6k%Yi) zfP%?B1GPsHIP(t1ax#sb$@YCADhxemd%kE`xM$8e!05J-lH;Y8 z%nDbVJ&M-tUtn5iU;(?L*Z6;eSPt4wO8y{sZW9&IYAAe?`ie%Bp`h;1##A^h!5*|*`IdphaJLz(t%sMMntJgc3!G7PG5G2~Lr5O;o;?M=(QRzD&j04^ zqgFPtA^nP$uhPTy{~PbPtqi zT}}MJxTaYEb73Cyl8|JCbitt4p25MGnFhbl=xOQcFlG+Kj9*f60I!K-z`{r5x$BYL)X{rlJ~#cxT@_19QvRw6Lud zYajUe*$6|h15L%)|F8G@Cl2EhMa8WD0Mv-wX7z#Xo-&j?7bS}FXU_^N;g4rlIC?s# z-Y@DOZBF*_9TNN(E$E8+soG$IX*i>#COdl!{op1l0xBSf9IItITgJ9|7H~lOi7wPD zRtA`-q-zG7GiHKY`D1s(0uoW-ya*IYt&E7Di)ft<<-Bi}328;&&sJMa^bV)J^439R zxCD~|Q6s|%RZpSL$R70#lzxTBJh8GAtv+5*T;KDgfn=O}0nTRdp1v?EW4^#$UxlV; zco}N}XCwc2ookRcgSa8qZpW+cO4@z66TEYlUnH?v_!D~-72bbV4%ANnufSk6_iwTx zOi&xb>PRCFD7b{4`n|5f{nZ~S>Oly#Xm|UOda&hpKr$#K^o$fov{clus(&A1pR^Lk zfHCdRQnqu4sj!&6lE_zmyAS49y%18?Y*jQJ#i-5ZkxO+GatJ3Qj;kBsPwoC#Y8F*< zkMt~bC`$+IfVK{0yJUr*wO^P5E!Dr`?%=4er<^j5 zB(yG(b3)}}#vZWVz4`oMOrsZ;ka#racN+3BYp~Q}sM6e|7;YM>s$<1nT&E*2Gp__( z2w790#-M%h2_K&z{j2S;*K1-PmJ0t9J+r~VLaFOaf^BNV%~&>KB7701?k38#M1q}} z_Uk#A292RO@M+w@W@H^u?Z7hjhEjk(0HP$U7I>)LcaU`Onx~z?+osI2XByIgje7eF zT7qR8=iA&^G}Fps0?$tl|LL1O#8f})rkWRnn3A^_ZPLt~)leYv zFE*?#p0i$&uCYTO_qLJp8qZT+aPhYJNjE-tF&A^1-sUrIW4HOGfzP2eQBYJ;q)gc= z|E6s1#?f)KuF&tQDcrk{e7+*^Z0=M%*+9yBmj|<@8?K^X-?pUMcq}UoHG}2;qf)KYZepKQ>P|YZf$D|Yc{tWf1T1W&E6zye6 zp-Ja|eS+{pAbh^E_jm(lxs>wvo+98e@w_Z6nJVHqnF>5v7Wg64SIbGG{MjzID@Wrmy zgV$CzQm_-U{LFS|Hlhptem;%k+G_^_0BQ&!3K{B3xb%g05gN>Q0WiGzyfAv5;&9yS6FV9p57!pj^X(DshdB~xD&ZH0m1;zbV(2y3rChO6Ka5I{&)19i?1N%DzS zbW4^NX;#mwey_Y+QSujZTJOH{VG0nXHMwVY=ph2&OiEzo%EOQ{y%8)J&wsZnZA2TK zT&g@+1hWWh>^)#KXl9rIz8v&eJoUE=G*%gS(8}3=MbT?iYx?e6vx~!O&dsctVkT_J zio=RCj9<8$s;-v`%NI+*Kk~n1aQxFa%|d1<|4{_L%d2N*&M|D@r2bjw^fXz(aud>2 z(kUwo&+^(fbAA1LMgWgo{|QyaX3tA3_0>#ep`*_IybUJe);W^6^9z1QvE z0)$Z8kU$aRcf-f`2=SWPI$|+QFW)w9fg2Xd4%2L6`rqBH7u#nA^TJ=~8IP4uz|Vnj zE>II*h&InePzE_mc7`(|&Yv;XBEPmc|j?W)2U(+k>ZWCAFI9xel*2 zOT@t%gs>KLzDE)kWaZ}NCfPm;_gi46k5~1o9rtxcsk$BY9jgZgN4=^CRuJ0AXK=d+ z=uOmbOt3C*YW(-L7FEymM^pHt;gabm1kNi3dl!xZ{SH(>-nZ3&T3190u9pMhQR3an z@}9HJ-fNt+6{D*ijl&X@`wMHnCk(%*dKQ5mOJJ??)-PgniZ+H>y<%^1_ses~;!pZc z5a=o>`8g4U=S}OJn_aiV&~QoHj4vE$)Br+lAbE}#2MZ7N-TGkmemEhnDYn1XeuNzM zr@L1#2^c03Cl$Ef{_WNK+s>S_4Q7qG%|}y{%gWR(R7Y|^G zlD{wDucm%Ywe986_5p6{;T4FscoQ?jA7=X_jj7^KzpUCh(I&in%7;$EL$LPC%P3j5 zq&>9jOa++**PWg0ln1kMa2$9%<}bbS3bj!@gAkC0p-GS@a1CuFq~|P5Kbeg1 z*^bmXw-UDP<3D+WTJ*ZMyyH7dZadC;ufVob+{Y-BA38=?&tMQUaO9G z4ObcVuiSv`66fZD+3k^lgVrxWZ+L#NQ800@BVE`5JlW36D{l zIgth5Q`mW|Tf1!&!2*?d3`C|@kz}h@lyCf#E~NXIQ2~@_3TvMNM^%h#RD40ZAy_F_ zBXO(d?5RDm$1R{o307>0RovKqdkUv~<%ca<+8!AP#)^U`HrH(Fd{q@$D^k zw#8EdEmB}4>N`*e81zE+>vI1Arp&zZ#3<08>D_n?Kk%=pZW;cIfgW%mjlS5G`|;Yv z(sD=nP(+9p{j=dFzYy#ckwe+CGdZF5`bHaj)kX+%{MOBb?5mFuQ(K)csm~-35hFMZ z^p~`{E9c(PZ*OH0c(-!L2-cbWpp&9Bvvc#bYDx4+H3O74utzyHZ&fhL27!TjVLNFTT4J0CJ~O`}-PPTsu0 zYEqkae2WhVdL-JMzSP^dw+I{nv_?zs;4pBlpv4>L#7DljgJ1^2%J zj!}>CA8qHy^y1|?{S@DxXMX3%-cz3{c!tP5YS~U*IKg6AU-E3-H3IC8z0B5jRl*)Z z1NZLLYxpvh;x0Y{2yi`d`EmXJ^bV3X;&a2baPn8}X&-EmfQVr;GRo@*_;Et*OaOef zzKMEMhf2raW2^L<=J9_EEoou^%gO)*?0>`f4N1-EldMpc^kUz2+5<_wsB}IkOj3ej zrQwy;{sf-WpdTLpYo!&zirUj(Yq8EJ^V~h`xJ+r6zy6!PNKxbOc~Ozbi5eD(&tSG4 z?0ObBa_CwCI5$$b_nn^mi1<>EN_nGQ`^ZB^9W4H;`JwdQePsDtFFUxqY6J|Y`SU}< z^IneC7hw0?^;H0I!dK!upDf3)nOo@4q|zEtmCw~keZ~76p)#{#1avkJO3**|waIQTj2TZEc%a-EsEAE~Tf$yD{0cc1eLs)N3 z(R(Ep5c81+(4*Y;JC)cLpI(N0F;}k*Q$irPIbMVIppZ zf7rZBfD2Tz+=RuW^~vJ|1qI~VSY|6}Q2dLrHP!vovV%J3lF3aF?P5G{R~K;PQb%bp zUXZ7>YL(q&Gw&oOGj0Qoi3T;K4Opagxp*o4iM)}3^r_nP25)@$1BTBG@apycFmFAJ zeH|ltmX1pxWk2K1%*>oJ7X#8>Ewk*dJp49MpMrw8&Sg?ny~%we)FT%a<@}*h>RFKx zX=wPi{wN=QlH&D87G~xgs*e~(`(zS!#+d_w5;G(uW0p%FxHM5#3ft|(&p)#YLr&n| zc3KoE!a_ng2;&T$F=`Q06587OWDi~Pg;EK&$Ma4&Sa&Fl5+q3x4!>dDSc2|v@y`mL zv97WcNrbQ)Uh~>KjKwaz5BT>FbD{FcQTwhve)f`=h+sMb!Vj&B*Vvc;(kKL)4Fzt2 z)*>N1sk_uKTkB&4c2?h6b|)t%=OjiaW&YZEeWreBElQ(;i}X@dXQ5hMIhLBCnC%^Q zrXS0xEhX>1d5YCo84U?Q0Q$eVZFu*GYJ5#J9?I`k#3T`s8hbwYBukLf;1Q$4h)g55 zOe8V6&sdG;x9*M?L>Ooc+!#Q^PC=d5KgU^VuQL--^y4L*u3=H0)816#tykz8F|$dr z9WM7rHLI6mcqq+9ak{Swv>rfERteaQzXj(rV83n~WeS+lI6+sTAt9Cy@366MxGP(; z@Q-_$P&CkEXesfEqb0%$|EzzJ%>^m^_?}Y|uU*)UFqd^X-+u$pDQJ9~(Jw*pfi#2R zI`7g}T@@CV`ZEOky`lt$(R|zf{QRrqT{Q%Et=(J?GeBmyZ_Vbw$?YY>dL&T^{rrzc z*|uF_qeu6wd=@V;jZlwze+arXBCuQD&HFCuEhGy?MZBhlK-+Vj{dj{Y^Xb8spz<;? zguH_Zkxq*NEKjDjK>lihd=3<&gAeEuwJ(O0@B%si;+Q`Q!tGh#llBsfIXn|EHcye$ z%lu>;)q)9TDf%4z2E1r*b5%`36G-O}#>z@;L`X=8NtZPmPkFen*>9;0i`1eOwr9+Z zR7oD@ zFIUP!{+0-#-Jkr0W>w5D#Xs4{dycF0mVYQl+mJV0;W?BU^kg&Fiiv>#(YPEtf$>y| z8mTvOp2lsPclkz!n-~F2lLiarXbzBo3CRu>1v!Cqqy~{LfdPvMN3OH35ff9f?y0tt z;mnXRi}m#c_Rr3xl#q#<&(EeK%8DF!28sH{FZqnhA=^GKUC0y=^SoIek>R~7G`Wlbf;X_ zgmwN1VLOnh)*$z9vAYVo!opDmEJT4T6}%HABoN-sltVJ~cPROO#WVO(BN`@7HYeB! z8s2bti(umGQuCi{vUq%dm*2vKkog3tb%^DY1};H>#h_ z<)fo^MtG!<@A}z_X=IQ&KrR7e0F6?&p9j{zuBI<71=*q9rmGima9~$d_65H%;p9Z@|C+gFkCg`Y#hJ=5y$v z_}Y?@?h_}oygx%Z2(1y~;@}vK)6fc45Y99K-brRk$VfX4pKotKU48%P0)p)}sKF_B z!$}ComZu#v;movc48=_-&0z@R`?nrG9t<$zho&R&Sk>!dliwNQV~<&W9T^w5JRPfu zj*Mk-vGWki>NpK6iE{1-zGgMS);|=yvRgTM>*FfmQk&0i8Q}>A&1E8e%b(oIRIn~9 zSL6hg?MJ=)JMO%vTu~2wjO;w>ClCq;n0&mZeZ&yy5gm-_B~P%3ph0c55Z?%rbwRReotxk&44rmfa#Mx6zKV2N(p}T8Oo)O~S!4)|wL;tZV##9f zcBr!64s|ftsFTWE9n_u@6BCmpt|2Nd@SX7wCe&Yz7w}GD(rQR-$L4~&Tm4nLj}n(I!hC55fr8o-0W`rjilDLHHs$nRt)eMfXf!1 zRoRczc)jM2w4oe;;{Y>aI|*Io|9#{|cXeCSGq&*K-jdYe;pBWLL@pfhO}IP1P*U9f z(#Er-G!y5B460+AB=3jWpot+9$bTW(cQ~E>H=ej8qH4SSZ9K`fLFhnDsm&*y=Y|x( z3i&NfBu6fkhhwAb4RB{r+miqy4yvzWMIH%fM^t5-XcGG`$&U0$#(s0=)ta>VN*&tL zU7xc*Caikr@b_^U`ff%^M9Qba{F+b6`D|kE#*&SzSjxRHC~Oh93AXNJ?wefo>LK}+ zwyE&WjHR_$&sp&`>Xd%2wTO%?h<08 zMu<9U&7yqq*AWH~3$D4;1og5m+v3o)w&BN<45jhmBOP7?XU+u&Qmr-qOQ$9MMlS}DidW?>m^Pq6BKvc3G+&6suG{3%j+u~La~o%_r2mY z20+f9mBoAlpQe{`pNamTSpe>x3j>Ua`#+#sQBUeY%#AlvM?`Gbk5#jqjVa~$NU4yHPw}y9NW~MeFnV`Z~%F4V-M8Otr9=PeES?UozcrHQ_u1^ec!=UEkYNoAp)>7xH~19+n1FKBCr3&QX!UP9{zVn5vgn? z6F$|n7^?Lmb{&7XqyDgf#{`{58i`D8Z%$_o+M;pN?80Anf#*@45k6>kPLC{*rebl8 z#(5xtOfUR1%|U*FHXUFOIp4?#;RTh%fbSu%#)HT05;XZRFH-ujq&t^s8-6~?7>PBS zxvHLK;kDepT)^b8>l(w$e6R?UxyRgR*hVL0{i^ggt>aF0`je2#W=>o}!dG*~`ZrKD z6a|K09do>m3E~6i27UJ8;^Kny`20zoIP2$*c#H}B_QE6tZH8!jFFJe+Icsd@UY6EW z?w*0ByRIfsw9vl81~!X+mz5EoV)y}SKW@X&DLEAJE3J%tmGD~aT%V^akLM1H*Z}m< z!(ZaL-1gwW{IGJ$N0BZ9ZCs9>^2j&~?ZqK=#oe z2_@D6t^MZ--v(tN?5kndSJ$1zW*kWq@UFq|cnv-IFI-$mPbK&Ua1114WF+v0XQi8? zEPVd^pl98SxEz}aQZul<3tsz=(}Tf-gRH^zns4(&x|V;K&JX?dGQzwr=vDg5ogXBk z&ocwl($aEOozh71g(@+oqWrMHI^QQdh7x*_N*#VIo!3v~68aJ(9A+sJv2J4_{g;JR zb$fu_`s>$%@8~sx!pm&TuJ7(M<2wnnltnJ^LEAU~fH*1m>m!qk?6wt%;;NxJR_IyI zt|DiRbDK3iVT)F1FYMu2xGWh)r(N*=^aY1@i%SQ6XM93Pu?4%SPXa-iAlE^{0GOi4 z?(mIsD$(7r(^{!OC*(l8!h`z{1$SpQ!)xE+f7EtzIij`hoxML}$k>;4f|OTwFpSiN zvj&c@hW?;fyz)I7%@W~#!PButz|gy{_K1arVlcTh?W+bkwXa;C$|PvsCCQs~Az8h% zM5^YPi5sMcl!G&Uvt=oV#auO%0>ALz#5Sobmdu+Fy%5V34W_Z-!-u4R<}2AkziSAm zP-1x93C6=<@kmmka&+>etU1oVO^U@Bk??zer-nkY_}4OW<&yISB61kzkNH&rB71d z`)-!ls6Q`vOw7iHp26v7Pu8TL$@RVAC7a`7zj?fl3=RSprIjTbL!yR$#svvXa>)oXEwYddaTHInB65vr2|}K zv{tJhO&kZI?b7kdwf->>YKH|*AKth@5+6b-!L{yf%B#dSagU-mv>E~Q7}PW zjc$iN*-3hzTa8-*FL@nNAEfSRH1i$d!MLz%ynY_*vW@?ka$S5eNoy$_bliW)%LPXy2+VJ>#sX&9n(}tM~CJNOi(a4+h z-SaK4q$dTSgUJETRk+8TQl2@Jn;y$@lJDnAUUJDv60I!DZM^v(^g|Zm49Y-#Pu1?t z<#9AW`npz#q?svp0^50ku6(<=ftJ<@#2eUeer&6XewQ?lcrAVM%@AO+fN=g0-eK8YfgLQ2B zW4$46>s2~Vz9j={1xXtgNR;i{;uEIp5I=p*wdeQJ3dgZUMMbqIUQi8-u_+WM`*B8w z)a=JNq?+`!iqjHqX)!LmdlUf`fesEDT8LL8?`XzgXObXV&@sA_g51QA(c;zqSDNDk zfybdWVtJz+@hOP;T*)^A_|+c43HVxroLa)yc&5Cnmb!I|&CVu=s;>6_-?d?=qXAH} z>54`u*zv$9#4lUhrKJmH14g8>4W6VblYsq@ur?tI93;P+qmR_sj zxpqgm;7Hpm<=IE3X}5ohC=PgXj)8)Q;O(J|hpsUJi^d*NHBM&=?jp@82Fi8=&}qRA z#xUjzbx1#N#s|^2p|2eqx6ZRGhpT`8=8zzJ2@lqA4GSsW{>GLHO2RX$3Yfe%u^59` zGI?Kx-jbTW0{yD4;L}Oo>W%-6OYnynsre)%5z~z*kn(Vc#^)ecG)ps_P)q7Y`xWx2 z$Mq;3@*Hfk5*_jE)rWb?FA=k>kQWCO1V|qgbyP%mDj75s8JO9`a7Vw`L7+;uPC-Gb z5=Abyh&ea_I%jHaJw|_A3@@zt6HRRT=s{)yJae6UG+O^UHvYk2K$Nn!R%$9Mo^7 z@c=5KPd`yFkh1|iROuRgx{|qc04gJC7eZ0$pj6Z^qr*L=;rJLPqxAm6(A3lvqMgmD zK|clM87C_zb3aa>mz3@M_cP(jUwa?2cR*=5#^F-lg|nL1DI6Dg*pZxB6pHqBvoA7$ zbI6nZRrEF9n+RMOy@JxJYcRKYkiUeJ{nU1^iY~YM9;LBW5Lb4YSdRS>4r^RK&w1=W3z-SC`h}X z(2`%qJ7`rSOD^dic>ldO2*>*ed08lYl@B~0Nuv+!P2xQC86AcyAe8XNL|2DS((Vc% z$=a=C&Hy?5#^F%Jh#|dQ{xpFSFc|W+vZ5Y4ST%jCEo%UOED*vn8;C3kSvnkr2?C4# zGD<5C9Giq1eWmcb>8_Z}3|LzFVWcbUR~2TICPwh9hNQhj2ODUgi5jAfix{znXu@_~W@k=Gh|?g)&b+L4!zw$b0ND9yo0$gh0smoioCU7ftG6?Vs`7MY**S3l5s!8469P3b zkPBY)(nRdK-E?p7WBk?o81V%9QHYH~XVZLR>_A**7C~Z>Y>RulJcnLdU!g29_ zsyJjOox{xM&+xPCR?o?B0+v*m2UfqlcS$gNPt6l}J#>=Bk9IErx7r3(P`7(XThdLZZs z6GVb@QKE;1?wjFUiK?K*Oc}*Yx|+d;fv&lTdG#Oh^nQ_T%^|&+#F?G^=l)NK=g8g!4cr~#tC{3?%|d5HrA*0 zlY&Qop76}TpslyLt2pp30o&V|?T3M_TgeV~*hUo}= zEX*9Hr+YT96;ggf!f~b>fHp1oDiqrp@~+9@TD3a!w(k9w9=+7@-1N$D-jj>PyOK zu@z1uWHiZFwD0w1&Uq@!86V`Ju$!};abu5!)}nHq9L>%Wv>3xUkhB+f8<%#W8&F?l z+2O#Z@vYoRHlqmE{83Y4V3za)!nFx=9^MiW&je%@d#h!mpf453gf}C%m`4+Qwl~u? zf6c{l_KFWpLN$(58ZL5P)s#5{#3-F8X`R(F1?rKf-;a?cDdaO|SwiF14Ts!O7s~)g zw3}0$&XlCb_4e1Q*+C+2bZSG6w1Aul3zeihLfo#N5PWlvIrvw>g8z5sWi8>s^+5{lxJvkDj$QKztk_-GR_5V&h*cY{la&K$;N zU9JzMGi;^2Drt`F&hiNob#tcULIOO2jHV94wtg96lCh*@=`rf%W>stVD;UW+0u7Y0 z=KOunr^#vRA!?~&ziLe^cF^xUcG@vNo8R>RT3ysCnlM*xGFV6e&wWC)*{;r#WTu@z zIKnf;MBP)1GMq1Z1Pi24sZ;6vz&+cDpqLK0J$NLfUNQXJ4*>nKA6sRkD7pU$;}d|X zZ*J#i5Sop85S%k7H&)q#a(NDv?S z;uTF3D!*P7P%5M)lOtnjPRxv7vEB!FHI6#mwFN2`%6tk!ZsD1${1YfT1yJyK(~WQW zZw`zIoIbzg;zLUXY3syM8^BW%6_z!b(TDao5oNS+Nv&}fenZhr2bA}Q1l>lP^r4n~1H9BtV1o(Qj4wOCb zQ?Q`)Y1{#tEdB;VBi6ufEVsUC?EoDeY=F%felCcJc#lqNSYp<86ZHEZHvRz=fqH3c ziBpyYRMVv^ow?;`D#g}_i8ppzv_-bbf3G*s z`N$Uxh)8{0m$2bnKpe)2s4d7I3@lMt=rh3qf!;Z2X}^&`=zx)`k(1;KYEupHzu&*p zhv7;w4FDSdA#3?DQx;BVAs-TCG&JZZ8OST1(HQ>8n?MPiOZjF zMsfqLA&?x5hzzNZ6FV;A$W83dXtw6L_Z^)H_&r5^H7~}&!3@-#(aIG~HId>8T160% zkjN`Yi{JG|edUHfpkyDxP@)G@-lr)+n@m0$y79FkOT}_z5TA){xJ&|2KW3XVGwiFW zb4BWeLi(EO8CcwneyVhlRQPdk+V1|q=sdlV4ugk~Fo)_7AA|z; zi6JxJ+!oKvD&$Uqj??DA@lF}F4bMpL{lUqhhvDMCh_|j&4lJZ9XsPy}y>H^Vholr= z8C4FJn7B1Tw^oUi|9P$nVi5q0PP02V?EnidqWk7Lh%ZiK7@Lent2h1kozoY!a0yV3lD-j<( zPbEX+U7`oouuW!ynwb<4GxnH7GgDJHm`!JX3k{b?T}Lt)rTS!&Ac8zlKPcBh8(F>v zPz&ukXJsh!M$&qKah2N5cQ;fv`bEufAb(h8ITPqL%YcE${0!;r{Md~m8-?Y_X!@1H zD=zyn5|@pFppMQ`(qn_1Z|aDCGu*W0%3cuAiaJ@nZq{<2b5<B#OUs?1zDuv3Kc)YqUmzy+pLa2AZSg1mq~FSm=#d`uiP-F&|BO)pN3S1WfnW zGXWAi%8G=&zDTp#IpjrtrMFMt?SiXSuPFSoP#$=Khb2r*5^YjtGH*~vkc0i(l;fsvc_DulAD9C$sXu45zV^ zZ?KXaN?>?tK%amp2x)*VgpHdd0O_zvclK3e6}FtL16kMSMBvs?5ZG(~g^$be>#~gW zA7)i_4tcE1^ODzZKW0EfJ0s|6tlU0GD;Ck(g>A@G*DXF6GL-I#!o9y07zqvhj z{c4Zm=fhYBs*pSR=E4Ya@&Y%)FNr&iy5{=|I=+NOyr6}@a!$y?} zlwu(-2WWmUWd9X0ZXkUA5KoOJyBG3^i$(aKR*Gf;W;Yq-vYM`OlJSTc=g#exK#}5p z;GpFs_b)IL#fMsL5%-#$EMq*r;mXZcWyWprT&USRhq^aBJmXL6 z!`F?GHVA%s0W)MICLF&a7<+raHaT$uaS6bZa;`-?EbYWP_7q2W_8{WBmq-gv4pQ&L z+yLhpVrg!A1&qB=OL+}_I_wbF10WX&NXaZkZGB_P2}3J1J6r$iUaZ%|esS`Wl2_h(lY}ttn+Y;1 z2{er?>rmQH9J+|&>YJuO=ApJzy6)8@%L|bwmS;oOIhmGee#f1AHdw=xpt&kc_%-|x zce$X-)CmjchfAf6u-7d+c)KloU~)>Z0vl4}Rs=e2{OA`T5QoMHXMxIMm@%lfo#9ZFb18C)nJ&`hfNV3#nh-#U=6s z4ySVkIw9C%TcXCZG0~rdiQ4ke-N#sR>YZ;&Yem;#y4Ua;)?kp<^MD zQyV<~{N`%x%{=+aCTRNMDuYr-9x;TGuh93Q^*+cqk-`w8X(06SS3IrMq?#@Fe-T&E z|37jsMyZ!yP(tol-fzX1U`Ci@x8wUL@Bh9fZr*M+%jb1_!)kt;|F=(<87$(UlgcLYPz#q%d1XhoRX1~JxDvf z#tww|wf!}g2Z-)e*gtdS8juD)%ANG5BnSElS*u^25>otv6GG9Yz7Uo%`pd`-e#ak)^4ZT5f z!wE`bOmGJ~S%g0sdP|g*iv4mP{@uFyaE&-)O4yn6pH73ZNYnXX+mK-pI6|H>?l%49 z2|k-h^Rfk4=>ZHXguV``%*at%1uL_vWtNHWrXNg+8o2&5T>E|qjmaavrwgV)iW&I~ zp|iT$K!KDKXscm+ycV!~pmR(E0(ev|Dm$&Cz5|)EjXe>GZ@%?I`GjBy#p0EXc^`V* zo~usbPcX9!ndmBq!zJF;mCLSu@#~KFkI!_Y!Qt{DQqHQvE(VpvJ}mdny2j14*GTsv zCrGkDP7R{Lla9@6+IWsW;Ygx53<9}P>f=G$^#e3IhtRyv%A}24`2BVpK$$Znqu=C7<3o@@HuPrE9lG?Aj9uR-=LqR6UgXxO-7-SzrotJ~9N|%j_(kpa)rr z8KN(<2LO|2%IN_!a+=osReH(+%&cTp)ds1>eXET|K^WfI?n;FGjBp~Y9(Zhn99P* zl@0t9TCgg#ICEW+T(iOToT)`zbtVu%AbbTFd!7Z>UkTMVWoRY6s6Z)-X@SdV4s_5z zJKH%a*^{#DU#(LL8h^7}7xMs}WuHlWbT!=QZet|#{D1G=aH3ZfZM%{WAOX4D$za9Y zPpwu|V8j5pTn+0g`=-sO`V)QVzTH8j*s9&xI>{{jFoU-dlf<`dKbC$tD!_c_!RIPZ z893|_J5YvC)?9SM{<9B-F_6-iBz_GtPokoap8rA$Hy&5wWmyQ#DD zGkyq47KUS(4Mo;{xc=&(HT%uxE_winixHRIH;L>;#fDMwd15*rYRbEI@5SVrD5X@% zVm(mY|9$+|uC0?u%C1@s6kuV5EAyZyV$*=gEs4Px#|k6MBWT)0LDxtvFj3Xp{Q2$h zk7Vo@Ze17t=0zPX|Jy{@tlbq!bcxD`r!B^HSLm19Ox7nasf6!-Z?Q0s;?t#+Nlyq` zHqtvvKd6@`vJtDmDUY-i+wp^pQrHHKEJsxzf`*ptl>yIwOSd6CW_@)V{ZdZKJO8Vy zY7fm>|7diE8;X)kYx~|!AyaQHduohbkhAW3k5JQ#JqEn_5b;R>wwTv0Jb0#fEVac)l>qc1$$kV7{j&JMaIb9S0oI}Z~e)$ zT&m0L2Ig@A_3P?+GOFAz%aN04LNIGl5RA~7`632VnfRh3op58~p)s@kQAqddR@Nh0 zGmPlTPPL`VT`Tncl?5obf|-nlcW{e*4Swy~Vtf_epK(VZ;BD-j_QD+%z|xVnL3D-xP7jRXDW zLPy#Wma|G3R@TMm^3V9)SPt4!zUc42kxve3rq5U z@!ew`?ZlmrESkri9&~)kSo0X`Y)=jCt$T$_PE?3+tOBmBo=4 zYmL%#nhTL!eF$Gy{8B4~Vwjo&Fe(So2Fm?rL2;XjDU|- z{-F0qu`P!mJ*{L(zMj=vu8%T0s&@QUi7?Pm$RR~9)^;+Z`@Y7ev5lc7 zvj-0U6OmQ5qn803cQ0fOgcjx^TTW()Y;U^8K!)xpO2Cr$=hz1s&ap7*XuL;e-#_*f zrY$ykDh|&Q7YjeGY0aK&dKNr*+!CNoiHZFl{`EXK zD$!#%Pp{~g;*=+O8&MRialALs)x=*~N6N^of?&y(@-yQ!#`$Jy2hYRXEX8Jc+aT>? zM3nlZkm>rtJr)HqL$qDe6)otKZgfa4?#sqRcIrK)Xk#qPobLCTnd!SDMCelq!iF-G zT$D7`wZK`0nh@T>Hg^6#6e@u=d98E0{)7i^f%PKA$dLMZFRLdzw;4+nXI0PnAGpk? z|2*(FW&y+9pJ?ZgXxz|XLRmYP$#M5YWW(K;GHa|LNF<3Wms!g7@&vnT*8ySsNlHq> znphw_B*+ECN$U*|IN94H2@&{jY>})JKqWtl^u*;YnEmd7Kpm~uifUzc#+(t9{qT2+ zW~)w{lo&UF`25cJ5}n}okGXj^(7NDgFhv_aS{y37mmr)HF*@V*2`t^WmkzV_5SpZo z8uc;EX7X^$iyP|Rqx6aJH-wN`7|wUIv;OCu!AH1s(7?c?7tH>F*C@ULeh&JU&?j^T z$yJQ)pcviZj!6QyiSOAMUKKTlQhhNXdtJh6DUpNHZIYKyR{r++j~xbab;0}a%SWKw*T5@s@#k*|_g<+Xr>tC!lLn?=xkC?i+a8V)%A$QKd{QxSqT|#MX{oQTiKDQ~EUF zhzH$<@H$4o$QA|)FDLc!#BHRrQaG@Y_A5YJd{=nPp2u9dj{2lI{dhiREIrL<7xAue zM^%vkJjuBEi8h4fo!V&uo6Zyk$zLk>FG@M`z3KAqh@6zO>kSMw8F4Prz;Z#L#toF! z?E)I%Xe&v{@}sWL!`FMwhfmXKNxrEjBU?KdEr|F>joJfS^PYHi69>*x5Y19h(^`<mNY8n1V z#4@KBm#u++`gGKvd5zyQH^Hs)Lryj=b=L5D0F;r zq%$@n{Fn8S-$RGa;NpX|li6TLDEawNX88`)Fg%<3msJ7V2;EZ#3I)zsu1j zZ^?T#*!*8JM5}f|`|FX!4uTBro@|K6M1V|JRWlQ}``qOD6q}UL8|-7*&d^z2F>3Uc zf(N8O(K612>FE$6z4q+1lLN!TY?qAk%TDuP&vmwqgz~n(+dh&eb{u+3DY`WWQe7@#kC{~w~>0;cXv0^DJ>`s5=xh}bV+whN_R@BbT>$Y zboZsCxpZ@RAN+iO|MywT?%ZG;Xh+sd{}TSr3pI23`{HyBDD9`MQg)0JRJK?<$ovaw^3{TBPn6oAx+9HL zlPMrEfSnQ=yHu8Dl$z(Hq7(GLvJ`N*`pWVKf{TM67DD~Meor3lsi_qIdO(ybI~z5Q z<^15Yozf}s1MKI}=u2mE@6+aI{*hKe11}!JRe*L7_a^LvY}f~?%LcR#>;sYDQV8m% z%ulMTY#5K=!iTxXO&G#RLii^s_?D+-@SwLzm=!YfWl+N6}9ej|Bng*M0-Mt_%# z`)3h?Cy{RaB6t0|H4u0&o*!k6K@jFT>Ga`_&0ex~7IR>k zQ|hMfs{LoNO${s4t6K_Xz`*A}{WJmc1XrV~&!b44sJxDX_f)aG8Xd|b67n}05sp0G$R91X%Oshu=O5 zaBg}MqYx4k!e!(S_=?$vfrr`ID*h<-ChJ6U*Xp-#K-p?>Ilr7$LeRtSRO{}X3T7pK zNOpcR?vmp&2C^2{2OLyGzXY8gi`+9QXq%@CC)6N2G3Fr478KBP*i%0c+wIoj^qaDE z+Cn;f6t&X?{KlE*wXJ~b(l5L<9SPi3)YG)dnAW#g4ltnqa!=+p9Ua(=KpE{|lbvE$ z%Y9ONT)9q;la7QUWOI>+4?OQ{(6iscl2<+>dbvN3>+pTMu`CXuv_C=j=_adkt*zM- zjrWcDQhv2}jC|jLUiybb8>~j?$@W?tPpDA>r4WT>CGKJ=3zeN%3|yP@QaOQZYNPK`3qR9h8(S} zzCc3&jT4%p0y9Ol>+Yn0^cKwQ_)awYn{glmH%vj_^L(S01;b{BzMfL(wQRzRzu~H? z>B0o7=RJ?Q7|ysO7G|Vc?8))jqMu{Y0`r4@NxRlV>U3VnK5cD-V4JuP@5CU$g6i+3 zySq5^Jfr@?U&%o2{^TF{FuDug?p0}jdmOOLf8e-UCJ40OJMV6;ZE2(| zn{R}FqZ=_xQR1|slUQ8;K^((;tXDKJXpgh3_i|wLR}sXK4;zGG0k$u%{fkMk_o&GG za3G;P>AdHtwI|f@v}r>DIPg{Ruf5$W19DiTv`P zUksd5jpqT_q=Piw6*q9V)A$_01YLb}>BQ*8R!FL%GiGfG69g>%;V(9nv=;V6^gT*=i z+u$V08fT4a&;q$)sCHu-ZTd9k(8V!TgyBam+8!ZXgP91f?Z*i8j&kqd#}O)TasX^w zJi6Q-Cu~MQynU{Zny?_b*XgDivVW%-2K5_~p5b3eAZBP28@q9ZEo;@)1HOiFkiU=) zOZQy{btA;B*gmk*_70g&&)A}M!f*AMD<%X+Ffzp$Y-suMM)?<)k>b1N7RzLY{R@k~ zn^zY0cbH)}EMZpm=k(x^&$E_LDf5NZ6-$NbNuyBif5j z9LPeLavwZ9t3?y}zLyjV3zMW%P|&S4zGf2PnCJc;;ME!F5_lGA`=@zF`h{(IM*g>0 zYxGs{8bOO^ER4kG#LpzE^Gc&RrE{Pu2slUbBTPJLw zgCOHKH;-vXM9OTS_}|qpECHP6&0TB!4$$jN`xOi56o}m)yTGH_y8?r^*1Pf{{x>yU zE)!c)K!i0Nhs*5)LV(Bc*!#SF(qHld^h@Q_v~9T%uG7==BNBptM)Z*oNLJ0$Z_k0N z@DeS9M~VHGzaq0oqEs<3NePCD>q!AbH2cFrA#<|}9D1woySba&BORR8afirCN_Bw1 zJ7Xii4C#N>h6QRsK71*K09}U*wh0mU@`|X%d!+Y`OGJSZ>lAlNGT6+mh-=OB#>R;r zI8Mb&e;%fe{E&Vs)9nRWEZP@y>f>kBpSPkzG{`5t#++wd0-4XB^Y&2__Orx9iY0){ zlJ`-Eh0mYf6CM(1b+Lnn$h}`)_VP^kyZ}Gbx%%1)L_l;KG&f8~w{`>g>xG*ILe0+G z$M3IlBdEJGkbm}I+NE`U5p8B1598z@{K)l@TPKX z;C*}HZ8pD!GUSmcoQWa_E7pdi@^`oSL4Mk?APW^9<^Lo*<#^)t$VTJrlCAJ962AZ+w|VlIz0YR7)*Z3$k^69=i_ETXVQJsfKKa}_~X22?0{!=;_Yy-NkJ@#Hr zuyLa;lz6*TCtFA)?Uf*MEGy(22B|4R*}~N=lpPfnK9YoR zRW1tv_?ZMXUfn%_{*~bHjSdXg0s)z+>hjeg1_Vj05N)IJBPEKe1PqPeB%gMoE#Oiy zmkq?$fZhMaxe+N!=LR^98xDnm_Nb|FjPodDT-(Sh#zpF-38RvK!1YyJf$`{4Q~A&$ z-J~x>Cp%H>@lX;T<~ec(M_q`h34i8U!xA}Mlrh#zdFG)f&^Jz4I57I}lf#Al%b}Gy z3tPP@K_4$Af?Jm4?vM~Nkj6pWR+eVb!M1a?1ZxbX@9AFCFqc1{y`fFWN+I_eynkM7 zy}1=SIX8UFwY0eINcgCA*wON~VTU+e$cZbyS3cl-Jg$=b65|fM!gN9kRBl>bcNj}0 zzbM?f~?2A(h zKMxh|+l)AwL~1XxM|jtDMo&a9qI+C<4&db~oe6y>mM;H_9RIM%+1RBoFS%f6Jk{u+ zidZK2-u`~RqQ0J=Iw<=>bhjL)F7~$=1u$G(f2`I8u*jA{N|2TkiQiY67w%WrJJx?! zFCyo_zx#NS_MD&%G%<0%D!zuA23Ad3Q%x)X*9lvz= zG9>UM?J1~kQ$A8JOQFn?J&Kez4UpI7Jlc3z?=fR`k+Fw(XLIvEnPHZ#n4 zFwU50Y5U@+BTzwjx7IzlfmXlQwJp9zRcs`PiqsasXj`?(7|+(xHkb=?sKoK|HPdLP zcc|d?Wmh}(CHLGsd|ZujX!!X>0<*`4hrVuYtTwGn_3j*XMz&#Qjx@C4Q>LK9oN-1c zoN-4{F)6`}UAgiof;)c^!-D4bytVSAmJEYf;N|hk3+r%T2au?+`J+2b2+4~;rJ#Nw zdcf&4aJ>8{J+w$+O@hJIwEQgmkZ9;hq#L(!&&G_<%W2x+Quv9(rh5R-VPE(tG zQ`{i*CP+bJmLCqgd`0osonv+}kP6dO`6 zr7{_tm?#?USHGS*4!kAJpC?`Yw1(LVbW8da__WPuqcSQZsOz^F)1X~_4%U<C_w}c*(IcbG?+Wt10 zmnGhelL1F&K-Qtw}VM)zFK@pT1QjPeLDZTarS9B zT;l7f`#N0Dp#kr*gvaGoTd{w6wMqY(b&q7}4`-eES#?SGss2eB6UJjzH+MazCDLrs zt`|eFdSz09YG?M9HQU*fyDVp|J`!AA$gZvuxTFoG*Bs7&LVbRdCCTlxwY8J<;th<4wBK@& z;2TFICE$mCiWULcnmW7;O3@KT0LOs5h*AG-{VQRf=qLZXA%25M|K^07aR7^F+0SRq zPR+Nx?&P*95Pr3CjY=f$ri^giK-g`nzvM52*Yspa%pp5=rwQpG6ghc@f5eEf`P7kA+0Wt**(pbz4{6rWz`gUc+rc==bKW^bzt`Ug=YBPvI9UjF`jso7Ct3js5GI z5<9|b1&he#;D@eEu~>EwI|mZE;zd`i(ll9og-z$J)x;{MPt{B&$0nurJid&-z9;Lt z>FnC6{D&#=DpnhlessH2OZU$kbXO}3b?UpikA3lka^8(oM~wu3hBW%u;K1!5SWf{r zJqUlyL$S>9C-v)dQ2R#Z0V9{na`NO6CBZcei)6Rr2#ZQGp;wTJU4m+}j z9zgG>Iv5}Vv=@pMhFAxWn3R$ud-q+Y0`7o|OVj(UVd3#7!y#-ax^SY@=XfF?J z$v~+0zCOp45hL2X@UnYlXlO!E zno}e(ET#Jj9$H>M$^E${iqEmyh|MPe!-9gH@(_H#pbEg;&ZhJiCo*|?8|sa^S)(Dc zX*PWF#UT8`_Bm4WNrEDo;(1bdCS7~fZc$zDP%+ZSFo5Tp2e6Hd%}n%{6n^sli+E(` zRcz~f|E*D*PuV>Kv4ETGY_c7C2%FDzpQsF z$xcx5m$X-kpf~Yh?91Rbn2)cdiW%B=JWwXpIY`Y*8?g-@+csTgqkR@s*9?($Hwr$N zg9mZF*KIjEeBJgg;fCy!?0c$18FvTL$)?fZf9^BF3gbPh0!#nKr;JkN)ZXD^qbc(= zw_~}wqku;5=b6c{k^B?pb2BJgf+Kws(;OPfd(8EcW93%=rVLfe$#+oZXOa+Z`FcxU!drZ2HW>A1%D|o*^yLL#lEY++mqoe=ZKHTk|7Ak8@v}CR0IL z@q>iPuazXv5;|_0ii|iZP8SiCjIRq7kTpA$mah4}`r>!aY3$LCP;WOoZxjv=7q0{N z-qx8IzeJOSn^&RyYQF2{ebttyRJFN>zdHU&X>FnsY4T#CJ6T61QkdEQPYa+l{oeUZ zXn99(X(1|1X-j~4waH4f;RB&%gCpm!D7s%p`xVb7vnPw@4qg|m3=lS}!cMFChum9l z`7B?+*y}QO^#F&`msJ(P?3oFIIM~=;mS3JPlalJcZ$gXrerv{Zf9_T=B&!?T-KF55 zsAyU^Fn44Gw@rA>4%p1gQxIc!+vV4GYI$SoxL%9U>^IPPQEpyIYuNs<)b;}+vZ5j+ zR)I0E)zYJcZfM;I?|VDLPhf?nG;WQ9)8s=*JhtLy@pu)paA)Zc<1cM0WgFL6j3^?0 zB@dEx7!LaYZa!<@|7|1-THw8nD^&^ufsk4fL>*twGvHS+O$eWrW{L!Fgi@f5LbX3| z_P>T%Z}ELo=u})C_N^*bmUQ=9#H?Glo!5Uhw~GDXshLlB@?%z2;))2D$_IV8h2T$a z@rQm)xhOGnr#GkEignLxr05?jW)SwPeX z2LA9u_OmGk$tz-XsH-G0q<(L^tZM0|>kY_$NgC6RS>Jc+-gnxyg>Y1M#Xd*`z69I* zhPw3&8lkT>%O)&frDCp!>9jIOA$wCHjoYWEztGB0dbjCA8^N;a|7osmzDCh7>mRIi zmi1it`Zrk5!hWP(R{7hWqvJfphk&PyBw}LLXgTXKM&l=XI62j`?xR_ z?f+p-8B+cE`VngfE4EzWMmff#F3{3XwxB((d(0=^|FD$W78)>OjBj<8>v(F_C2Ob0 z-@q^6SJT>fbG4(d@4~eJJ7@`7f&Erg@koU|C<}5kw~_9lqJkWX)MYJCEi7oWH$0h3 zt|8_{Xa9G=fqg7Sr4k7`g_Fg$TK@_z_r|d@$}V}Ed%qutbKhOawRL8$7q)Trg1-J5 zcpAxoFPrj21Wv|RP&76s9C2HJh>2={?0zLh)sWhHdevhR%~+1+yJ!t^q)SwiDof{u zL60L^7JpQ86}zne;Jp+E*w4Q_NwSWXxIqYzogbRjGFi$*yGv^d?PXe z^-|v#O(~TOn;K^j(^8*O8M@qG0k(rq2Qqg*&d^!-WH-Vw~LFd2D8Ei=DJiFpy51?Ln>CUZ5% zHaQoQwL0rQ9~u|&+3oeU_A0HRl~TK-5|A>UPOCdvWn6QRDcD#eLxW9Pa)bmNZRP!w zF%g4XDl{`e&lFwdAfJ)CSADR}hSvAbpD_?6{6z7+oP-IdRu-DzXm4Q8E6i6?FuY)m zxe@<&is~wql(*OG9?CCNoQ)ox<7h8~WpTv6nCYaNdbBS%Ju`tT9kS*xX<<{x;gl^m z%7LI$)&3hJu6wt&GVFC{VB9hLK;ZzLhZC1mzN!d@RZEyAJGTw4M7-~2NNXFcn1qLG z-+a5zE}12{KTlazAKgT1lcEkPaV`F4%A3a({@c1?#$BRpJ?&oIOnF=mw%Fx+N=e^e zE=wLCfi^?H+wse-pW?i4uq{(vv)KFL*B8urfci&$aZP}n7{k8(lwxGR+e%|#+i}S# zXwoI}1QdUEThg9;hHqkaaM^(yeXsD>kVRScwNQJW%n$R2x{b@EE7$%SAX>osZw!PNPOssg)ysCK)61HiBDGl&lwfykFn(#D@bW=o!Y=z?+z}!9gO8+JgdZwqiJ$+;a*Z8_h_>DY z3IkFLkW{>wpA9D7Z8Aoq^wW=ujiqFxCMD>us1{%JkBZc)mB=bDMwC)C1m1dA)SNM* zJW=Ql#1)TjI!qBUw&$BRbcPDDpfmV(eV$)=$$%7U)z?a)*}GLosbUfRTM4Wsr_VEG zSnN&Chmn4e5`}l$SGF+<YgB{Xtg9*x%+5Uo~-~M`; z4v^X@NOmwOJjvRaPIF5GO@tjCo_30gicvJhthLEMaKQ!vkEj7Zt1=qX%8gjemrLvh zg{1%Aex#Gcy+m^2l^b^SN+MuK3cmZIJz{iwGW|AU_c>yIhj}t^8u%m^i(WbSh0D|5 z`cif@NNy7nmm!z7c+Z5QkuQ<59=g(iJFtDo(-_NhvouV$_%EFdtfay;yfrK%bF zRk&Znxzylyu*mJ@4_gpPkZm<9-Z%6EKf;yA_|QcH!ddWoBmHi1!eBy;RzQGdA#;lL2~lkd6Z~1tD#Vz-axgN& z7N!cJYZY76^o@TzfJhamHRV`Hs@+IJ)veVOP{1DpuoMocf9SHeQUnPiS1H5mE%2sX zpi8!M1UZDfp^55#;b339Qp~5%JkOqi*X53>T3rT)Fu);L*-4&WrvDUib@f}R7w7oU z;G4n&(S;!2r-7&3fQ_>;8!tx4?i>~WSvpZzRbxFe5JnZnpF_$fs78(XtWEgL3t!NQ z=HkZoIh1^!&=D4N@Q5fH7S@0(+s$}8e}~~?Ybi`AD)bd9G;9hZ z!fnI59{IBm$B11?Jo8mLOy}~C3!&LB(z4;m8StezQYi3m6Go%>K~@0?L&VaHAa==g z1)AbKS_S#ea>w5_6X{Ae;dJut?tI1J%8HiIMN;Z&jlQMZ!<+806@;$whzZ@_@P4}X zr0_8yQiLXwE+OhI*j~{0yv5?63V5zOa8MWLgb#KS+ypAbR{ICc3QQnF5DEf{bo_*E z@*vcS2%!tR3q=Zt+7HA4BLA)Nup|EA2yQ0VO5rJ_8tB9`pMwX(?HWqBo&0NJR+G|+ z33>F{&ID*_j>^oT2N$_CR4jL8aP@oi`FYRk_TTk+^fxDtq)%#iPuzHg@@T6Yv%X#r zcRS;Cly)#oz+xZxAOiX4%$?SAP51!zzvVv3=!yzn{OlT<)|(so=ZWuPBl^us&EyfG z$L?+4ntE4y^l#(-u%fx=voAehiEi^gOW)ktmjYOX)#&0QOtut=x$KMS46;NPG?l=3}WOu|in3q2kPi^y}PWb~3PkcRq>tV}7=_(m-hw=q*1xO&Bz72b6 zN{z6xe@QvdJEph7EV$x~JKiM{Qi@S_0|9<<`-?x2*Jh6g45ay~)k}P)H8Ciy@bCEi zs5?oaqN;dwLmbo)fM>@rHna_$b>98^DXG~_bS_ajiL;I#%Bw07YGbnkQub89J?_vqG-;y@2QORUpD$jcr=iM@n4(kB_eha0at zG{M}cj!876U;{d2GGg>q({bdSUS4%|+ZFZX1P)t7&vj*vg7~LR=#DGrg8-5WRX8tY zI=RZM5hC0B=A)6>9%;ubcgsYe30X`$#AZ1~Y)+Rk=Ii_2w+5KBx*LQQgK0ttM1RV1 z@nTO&x`shvE<>p$d=x3I$hxfDcB#UykBljIFeJ2_(+Kl>q!mud3%RAtD&*X+?#3N8 zpEvfaE|SXm7%wUr&7;0thq}u#-g`=Z;GwPy6ZY7AD`T^omNKX6e&f#T33POSR0MUc zgBD=G!RY_mW&o2)YJ+^~c04aR3O$I+u;F0w=f*Amoxg#PiBSSF!@F#P2 z!yw4T-l!KOp47}Iy21~WSCw%4lPh{k4y*rb#$W!Yv@$LWR!U8fE*$1!;G5WO>K0BsOu49>tcD#21m?61)0*Kf$3Z2-{C;^k`1`etDx&F zvS%0&7L3Q&7p@uLo@eg1ef@p69+!nExb!g*;|SSJSlfisfa8mjX9K&;roUi#qC05@ zHJGIYF_+T&bhVP)@s>p5=sJ-$FKB*Hx(TuH(I*G(- zDlnmcDv~2e?Z%@F;uQ}xQ)5woOBANj0ZL%lIgQN@rNfaL)(SF{M`$aAbEMyryMvP> zOzjcj0m-t)%E%*3b#z4^!oe6@fJLufC)rbL%p(qc2NMgy5UZH|$fO<;ir(;JPDM&r zma-}nisq$ZEYEeQA&!c9!e%dj073+pq#UPc-n zrq)$MT6X*`=uFy@{_dA6o>C)LU$7YV@|$135K~5i3%V&IYH-+o1|9?d$A>f{S?c=_ zoQ59Z3jw%^fiX!ete$HmEx_#rtezXe|-7ac~V%)d!` zGr4HUtSk$>FvI+}#0-+2AU0cNsD1GUg&5q(#+zRSh+eM|)(5TARIok~)Tb2%wOOe* z5!}ks-A@z;%rZ$1+&SqoqKNqr&P6|cVZnSmw3SAn9fsGi5wd^T<=kRSqQbn|HS^%q z+mNTj=*o*BmL2d-UTT~^e{pU*hP;+bX5xTrf^=f^#$e4Ds>oO7Uun58YrXtyuyds@ zKeaO9^49nL+!crq@R?|gr>X<31rc*#CmS2WI0K-7A#b8DH}%XIz~n>|`E1V>2pn5L z*?@~Czc!CSp%NF>UC6+%ll`pE^IV}!X?hFu!by|GHZ- z1yD1(y((9mGk6k)Q&*nr%5P6w`0KL#a0hIz!AkoL81qDc?~9*lCw4}FvmbRDoq|8n%EZ%DdS6pEH*y&ee_ z)DIs%(Nd#)9aL+l1)d@Be(!t90I!nU1^$~Y^b~ej(*wse0i0Q`8Lz%yBgtmy3=(&Q z?5S9$o!7DxOz+grxBP>SIr|puI^hPo#eK9HMCKl$r=dKU{kx$dF)Nkw}^tPI|$tl z8u;aVsvg9noR3VKMIZJ$zHo|eNn80sCAidP15OUtG7IMk?C&+ET)*3j`07t{(x1Ww zaQmQsGd zQSgAjbIAMeOtSpfbMAckpPri4|GhCtoYQ(>Y|lV|8Z6tG-x zP!cjVNy>Ts^?ZV+X>*Gxc6GmYlDgDIWXI*zhq6|@{Y#=2q}9002KzHOR}}G}L^^xB zqlu@gsZN-@5S;w0ufVQKXluB%RYu{`ji>8^Y)>aCWI*vlHlJ|9xo@gXUr#XzOpNaV zLjpeEog~czb&r2#5!Xye2hwb7<>=3tk;5?jYkoytSpaXCim8dt;q_H~#`#i_TTpXUhAfVA>pUBGGI4VrW7_a|e38!K?sIC%TL9b&IO+hf8wW7;- zK+nnIA`_fL{H|{(n3=?bq5@!r1u_^j+c_TXY~0S*DyBQAYS}o0vga;qIUf=K^fJNR z0>!6m4t>``G=vtaI<>k~bCJHo$ z^Rd;}HrOnc(*4IqXGr+WtC9tEq$LWh+$+uq3JX1ME z>4@u6@Xh*`7ZU`SleoM&^DbRHytw3!yJegm6AceC4gknm?6}5FYw&~gwFaj^pU~O= z+dUk$4U{^EzQ3)Ip`S4)T*gi0=#=FxK%RvaHa(Iu*a<{L5K+SIs>(tN!g7tJovBCL zg3pb^78ou!m?Vz#^PRLQA?1BH{E4rTv2)gR^u~R#e++NYa+Slf?3@xmG;9F}80}O0 z9OuU2A>Nlxqsb@@L`fGFt@NXzJ7{;IJmtUsU>Td-Psmmbk&=8E2v0)8`4LX6;M!~J z*K2~!YZi!O6E=!DU6mkoSN*6a^9pEqU@;xeLI_}2pwI(TPRXQQApCAV2gUO z4fubcR)DFP*B~ViVgE?TZ(}_gm(|G#b{Z{;ar7huZNY~x z2-5#H#Wir_>eF)jP4}tC#pm7g78+V~YJ%^8Z5S;`1OcmXWcMm)Ks%$uOK~E6@_Q%| z65b-cUnSX)Hmzl*}HeN)@XgiB@jc=f6{|hQ0YuG zmSo?PK4nMD_F4DZL$Eit%Y5K{I(j}2&YPj;gWHjE9-NH%mYv_I|3#9+09gOLyyRnN z)Zv=rApi58(-3HF*jFWe8AkuxX*@hG70M9L#Se-#(IM&G+AXIYdRkPgNbWw52?j)} z8&Q^wD?LYjGD(~OTAXxiacHa0uO`Zv6B?l>4fqi)_!ohz^2d6^U|R7h?)ZUKAs)D7 zb#?V)&S(bc*13&}2u2l{5@J=aT;BEkFAE3Cn+Au+?%hV~X;TRIVX&}r8xFP=;po!VB#XycpLwFh9VKr*6%n+Swp|jL{AVC1SD-pM$ z(fJ|Ar6nw%Xp-x4bNf_V4X@zj2asd~$BDz5_v1`R-G;RFcO(^FfnzZPB6y8O8IM!~ z+1cjp$9wJFA1CW!w<{y#^|6ypF(+$$SoklFub^Hfj~HHel7k7`U>a4t&rxWTxQu1= zal!(Eu0QCuYEFZNZhLfWHE>M|RLxJa5!u)@EHc;63K$H%6%>gVqfX zjbc{L!K0p^E%I7U`Sgc3u(PCuX^q_WH7qz9lu2x|R5d_2(|D0CPK^Hf{Q|0#TI>+5 z09V&by)x%1fd|H{I3_EjI*%GnXj3r`)$)h0AWi=JGn z^X>7B&ymFFcu=2Q1%DY21YuE)G;pV={8kSoK-6*tSk#;l3q2wvIv&bI&hM{Q&5#%i zLMMoW6vsMp2Jd9$@`rK^&E3QiVcLfGrVzzP$Tc&rr+swyBoSl|xOU7S2|a3MP833; zw=Zk4%)N636ZUdLK!gCRzcigLq=^mkMI{c6wv9u@M%7&?hVzK3d%9#ZDn|>bbA~q{ z6jKrDo+t{14Mf@9tToavpAX{Ugdp;;5w>a$b1Xc0?F`0Q_*S~ny2C#IAN2x+Whg#q z!Dg!)i)pOIu-cCQ7_A0$N_mT7_Q_T1qAn?BQWKV?waSkZ$VN5Y#gU%(81}8xql=)b z<5du3Z%&+7_;|ORm=RMsTEY+u4YvT+(L=0XF*s@m*q}ALw`5oJ7tM)Gs_!mr=rKSF z&Pv}9xxklq1<^nkMtMz{pnDj}OVbbSQmMfD(=wOnTHDfI80Km>Qs2~)TLe{c7vpaRh^%&(62x2Zf)uU4QT`KE2V~HgEc+ z=rGJYhHe5BaI@@m{_~JltMAca%IRT1c6RtpRmt{D(@WB*X(_ckjpw>eYJXRMAn(@wdd)8Gn5oMr9o}=CRjYhD*M2I<$Kgfhg~$US zi@~pbfB4XNsrt?PoLru@I7$5VAaKIVZ?^jpeH- z$~^JSQz;fXns!_$FU}d&GS6<&#ng^)k?R9w^)M*`lCZP)Cd z<3!O$b8-&KlD;zW-rC-fjI$lp$kb^ofphQ3k2*nS7SBojF{4+bo=>=bKy5CB`OfJ5 z-lq`jEpnv;blV3tyuFbUo#BmAwXQK?#=}Mo&vYq905_gqCe6F^{_82J`$lZYDX{LV zx^^rBA2!_$nrgY!R>cPpBXxBba5uCcTqEBdQ~2)1cT{&zgrLR~gM=#}_?bp9r9O9; zgl2VNnI+yjKA$LbF;wi`fmm?h_PTyFEvgmlPa`0`Q{LwyF*4La^NFxq{ghJrb zSJo2~Ef7uj&7?l8%>yTv$`f;Tzz=h@W4jS2nfvVKsc;MeSFm6cX;lqi<7wfva>HQt z%3UE@HX`NYA&~`@EpEvtHaaCTnRk(*u*whi%=j5MiA@@@yIoJCM26V$s|B}GfsR02C&E50u+A(@69Bmt^%Hce~%!zt9x^~My0wNV4 zjRXdH%!3_8c5O8PArx|*gTZABR*a6v9unRNWudFrzCG@qgm4&LWmf;fd)wJ-;r}t4 zIFoN8VUyY$<`!%Cy{+`0Wk=LimDdRo=D{c)xpxxrx3_;fQM4J~X~?IAo=?COfhnop zZH+ufW)zMase4idBv+q802fC|9w$<8R4r475B&aHMl!;1-=v$Fx<|W>-;Y~108yNz zXsDApFITD<1I!lyY4%wI#5QEK474?h)aK1`QdgqVKywZPEjk!cUvBf63}gz*F|&HH zcPlZV3RD2?&%Cf_%IH6MsH3@*RelLh_l19hme!k_JZ7FKDw--7<&hrW~w0@0FzTxvec4)AtXEpCv6Uk_T8D0DgcYf<~@mz!+IF&G0E_IPpM9l_1m zv2D(z`KCQlhDiVE0%&zrlgyrRCT%~%4k<4mn)<8j*gloyk9b>Vj}^s(s23W+Z3SoQ5c^Tu(kJ+3@e~!+4fA(-aOTNJR zKU{D1gq{2(TUUdR+PdtSU0hqnQ|%_pbv@5tG>4}W{+JViKgw6W!_t52))_W4W(1pM z4+<#94^wxdFaMyjriD7DO{6V*HprXN#z_e80(RByP+}#KtZv_CJK3?E^DC1db zv)j>+Yb8vP6&H!_-=`jvUIPY-HEW!1jAwU5RH7Wig!>H5uEf3Qu#=VX-1_ z-@l$lWy7H-d(Bn7t74Yn4${3AX)MR@pO)`cb|JvBL(nqAudft~ncJn3x~e7uE4{N! z*NmZ`y4{+Hf1vtgjnsLGH(6s`R!B~@2>U1lQ;Bjqr&Embwe$v1W>KA-h^5bSE(~~k zzXL>kort{%;PydAPn>dd6f?X-iMiF)>>GmYL#OsRw)|Fv(s5soqK=;d(&&PK+l;>f zzd45r!uWjqC!sI6V)r;SB+hB0Qu3;}7YB6hTd#9_pTALe;Z<#c)JlZWgMK`9G@LisIXw(ZXUkC75{FefNywy5Ry>o$7&( zKgU(;pNTzEfif}A1C}@KVA34*WCz-QLpF8~wXk zKPXAp?%jjmosYvq?6&UC(xAPwzdL2k2GQ4ls*;m3JF+ zL9{8cLf-`3^CDdBq7Ga1T4O{>Z~Vkw!RpbT7#b<-Uu*o_^{^|XM-iS7ZpTnU>F#hCES)fjs;gIKDf_T&d|81PGCaPhO=Z{Gp9WJ2Lh<} zRR8nMc;N_PqULbU4MBni(>tf_M+x#e7o^3L(?Ck$VDLO%pjbK&OPIi!!^@%+|Sz54ljzwxP2RcW)oKftsY*lpvWdl>ZX z)@Z;{rT;ZP&E?C(QCLO8!J<>OIrSb9gt;pK#$f?5&2v3^YGvgpuv{Eu>@v24Z1~~5 zfmnM!dZ7vs%D!6SFRhKm_CTUQWWO;52N^yz)6x&p$0H%$!~EJi4w%=tk9)%RcMVUvLp6pVsNof9v> z%||4E+|_S6cBK2Vqx?P$Phr}`I|^s@?hA7&+3-%Fj$!aZvS9ahs-*SnUz^S{?rZSN zTCfO#AJk*_#u|IBQ@aGRxJEu>uignPtgWnH*eVY=P75e$^3xt)Nk@Wh4+*$1+j zU`F&r@1V9MJH^v6(|Q$wQ*O+Y@z;bVju*34=uAg0XH*OKjM`yv4HjLl-cBv1f`FJZ zr6&}qS|T0NVvw&%Ie+Ow6CKa&fsB;MWra=I_C*Am@clmcSChIZ8_T z-pepw_P(*4np-%kdi4;Dy@=M8YdXcvEIPBWjQ=_kyYQdN>;--PhCS9(t2W&YS~|dl zE2zE~pY(iEyU@pdeynJ;t90x(MaH$b#s%n=2@Hyq^wtD_Ag@wP=)wZUiQ;qvG#tE5 zN(Z(M`=`+~;NQBhQIkI2I~H6VneF(F)Zx4q5Vd;^5yd+z`FMOo*!(y7G8gUts_xY0 zXl9-od2el%;qTGWkU%m&f+IAMIDz}v?fQD}fE(lR>R+Xm+NG0Y&Krd}KA9)>n0A*> z|9w5zT_ zn$av>aY$^vC~mj%{vmlfEL?A$%kK71%FkjfhgJUeCv&Yan?zoWRGMi=nlJk)$8xGV zsje3S-!mHxi0xN`T(?q&?8f-)-WFRQ{+b?7DYh40C0@BMMmzTDBVO?ut+PgKIL`60 zJ3vI_@V~gcrr1gBVetpSEw71MmLkvw)Pt!e&@nQsu%)S6DqN*CT9&Q7DO%27W3;YV zm+DIQn74#sPHNUl{&@Qyez46&F1fgey-eoQMP|J9%4?fVagYq}z9$1DHS6HbQ-1Dm z9{s8;d!W)g;kM8MqsI#Z93)0yP>Q^i;PeD9%U(Ktv=VAYdKfcV!a5#$lKSVtnw>~T zrH6H2x|4Z32A^w0nD?{qe`Gl2C3nF?J%tvRA1>zliw9_KlhdKDQ4yHnoXE*Z2hg70 z1dmD%AIxK(F!9k0wLdaA8$=qAu7Vu-G1;>s7oyQ+l#Lu}s5g@uk2d=LVp9&BcM^^BBNsrBIzz9CVb>5 zY?9vm^R#t@E#()g1n6yH+b}LOn=}OpepuICEf0gtpAUv zuMUgyeZF3n?(XjH?i7&jZlqhfyIT+uq`L&9LqJmL?nb1$VS)G2&-eE}*YYpd?%ea7 znK^Uj9zoTD*4glr1)j~>*eScY`?4$-+Gn3@DU`sM0dd0LfS~c8(n}{|#ZGXAk2Y9y zq2BW|J}s4PhtIoTJ#PiDr>r(qUMDN;`usd_au;rQO>;yx5>*DEo_dwH0kMGoK7vw< zcUvE-O@PHR!Cr;sq{}EU$_YEP%vbZz71HO?UTQ?> zB_ruFkTXIj7zc_WtcN}XKZIJ?F6jA}mtRWdMwmyRPG)u%NL77YjBuowGb>Bj*VK_% zd+4r@)UY0YjoEN3!fvbHN8xo#V$qi;cUlt?L0dT2Q$=_6Oikk>0lf~&56ln`xoNHu zTbrMR?5zdTKjF210iYl@X*lWWL+*VG>>(fRr5BU|lus+ZEiYqGb8_owC?W?Tj-c# zGyEvrX*I><@ic1shH%OY81Z@t=@T#d1;j*QC%6o3H=90t`g*0JC(n8`up^Co)#br% zurC8fcJaf{Jod?u0PW_b znI`i6+q^O?m*iS`LQi^*d9BW*lzSev9CINil&1o7AvJZ{y#lQ0M)CuOdy&z6VMEFFPw5 z(ArqYfe>afe2VV)_6ZyBL;snp;GWW^=^+#3^}z^*bc_w?f0(HGuXLsOkSEhjE{KK< zFLUMpOlv@T^)2}*)_K7yN%AzX+4v4pUqE0I$QCi5!~|fowIgw|5_{#I@jKWTXk(@S z_D2{nq8YczsP)QYu%ee7)QJYY8@6xwcw3c~A2$M}4A0S>_3XwSUVjbwa94n%pwf+| zyrlS@u~{RAsXS<8kl9W%moS<5Nsca|ps^%-bw);u!DshfuHWOM2J4SaYPI)-Fys=q zZru3HVWr=Z`Eu=Q?Ox~WLoMSUd&Jjgu;C|{Rwo0b`qyhiURlG?7>p`#vY>dR)nFOK zaY(2}Ak{Pm-SlQ^3KWDO#1ak~333=p24A$oc3fA3a4&-if>=xFp$wYXE?{pa9O#bv z$*0ul2oKxvY56FSzGzibMn)^YR7|cucI&S~weZH8bM6kR$5ntprce;jh=smn#scl9 z1{LnCMm9qFtZEkfo#Pn#q=Xddq8cdZqQBJfu&51=7 zC_$W9oWP%Z(%4xuG*#go&^r+Mf)JV10u!ExDGwduJ<1UaS}gwY61FSu#>DLg<<*%JUdSOCApiVFo)imMf z{Ew4cpIy67f^ET74li{;p_&pA31mtF5ZvD|eiF~s0_jM61=`!uaao{*;{)G?RS6A>x-)~DL_Q2t z5l(u|yVYjQCdb%PC#7QLK$edgG9o3knx)oIg^Tbpwl!wp;M~8}J>b0?i0}AA^iL1d z6=EKX!PNlBx_F{_E+>#+POdKQt0@>GMQe6hSt2z%rQiNE_D6I0%EqvX5R|F7RU z<+sgND>0CigX~-@>;ad`fDUME(aA`pb2b3EB0nf!_=_wM0Ud~^VA>1%xN5#!nE-G2 zf5pAUeM!sxrE}E*5g)IXnFUW+eL*Y`={nGIbDP3mv^1;E7Fyo9&1cyOhPf8+tG zvV*HEgV2p5Z#MfU{r}OuR(3+2RIbXTpDtlJ7K2b{MVO=fNmYDE*x=IU*{95o;L^8X zP~h{0%{oNX(D3txL2ICQc2(2#qbRb|5430TP6dtK$nVvgf($D@spNXTYO+|-xcws0 zSI&!>$EQ1I?}=60e*%Q&@CD}`80n*zHl;VO-@MN*+-C2-pNn^YUaE4gs-hjqc(=5e znH@Ps8v!akPcNn%P(f3jnVv6YEOAmmc>6@7I`KA?&M7yIkMo*`^&kx<4JYfG=7-7} z92k)=!i*xsF`WIIGxM*E>LUgJyqwP)50<*kY#r05_OxdTd3Vf6m&eC0)}LXkx!50hJv;Mhc*ti2p)4Jo`2a9V6NI+Y9;Jy#um(;Acu@5JFvMIwu^LR zdnzz1Y_hqT?;kTz{n&rlssLIdNDoCbFDGoxMUAVGmxojyWaMA)PRaL4ha%X+ZnCS& zzE2o`tD96gVl=t^r_eM|s7!Gl{n>u-6)S_bGlSy(uvBdCT4Y>iJ)`Hzp$mV>*&hpI@^9q zGs{E84a&s~((LN0ql>d6gqrdk7UVGFqbN?%%^0#E^dzKwOYxU!C)EscvGV?xguYkz z?^B*MI<4NQUA_nD6_yk)?V%UzIxWH3SZS-2t*nvvp$Gjjbrmkbr7d^)8o4I;{k^s6 z-oKwwXTx*#2ZMdtF5&3IR@Nb&`Av-HAB?ohl%hGX5n09+?h5-fM zfIJThHrLLbP@pt$W?6!IgGv|A7b$BK>$HU*!=PSRe7~DiHI#KMXbeEtRnt>iRbp|+ zf$VW8Ux@H>ot$kSU5@5!-0R`(+EylKj|~G581aK3dzXyy|fX_dT%ajHd*cmi~=Ep+Yunm>)h|QV+!|_#)gDA0QmQEhOAPxG0NH8-vN5a=n_4wsP+i58( zDL7bY@9JSf{CunSv?^ky?R;51_fVr%(TG4u7%cjJYcQm1Ts;8|woH37ul7UGbNl;r zpDzLzw@Y??-<|phh>;2(z7Yw0`E1N)`M=Dyq)ehb6O*7>i?v8gOxi$Z3E0p?oW~L=?9a-e}}lzOkvdvFO1}(FM1e4PIoL7S=lalw#V*a)e0+G!fOIY@yDvLg+a~ zJ6{PxbpB@#_$uf79$@oLxq`vRo>*IFTov5l<`N5?xo1=|;TK0m*u(fT_zB)@r**!Z zkJ{z-OBq+bC#T6<@ZuwDX{RSD#D_!h*~TmFqS9b%9s*9WO|R7HjRYlOk;pJ8FfA9HHz-h|EESu%!q#U<@J|CW=oIuG=~(iA)k9cW+Zr<39TO3t z72fS-y>V`B{QU{tC@T~96hAJ+KD#>$BIPpucDLIEb=&7tZo@5(wxqVPAfJnI&Nqc1 zA=M{@afD}8rx6|&pOvU7FAq?uc?qxS|5)^N@^?`K&@A)mG{c#8`ULS&oR~BCjqr9` zAf22bOC9;?y7L|Gq9o^A9?TjZl=HoB+)jreiZ1$)S}=if(U}8fe>Bv^?w=-QQ#)td zWg}hPvU>W8lxo9Im0RV5kd~9N<&0Tk~l$Pwgs>Gj) z;j}S45fuVc#uL;3jTKWyd$18rJf9m;pdlk^ElVrGeXWR{E%YL2(Fd(?4I6xi2NdSy>tF&G1}` z1uIN5!p{xx??inO395H*ef8vTRE7P8RqJg4oAm%Ti&*=oxf;>78kvV}DF80$ZG88& z%5!T8O}tkY!WCu>6sHJ3eLpa28#bG7HtD_>VjplIcKwwXM8;mRc5k*REa>`v7^Fk+6T0jC zsK|ZKL9?}r&msE%>(FJHN_+l}%cPWjk~I!|;4#I+hRE1)vmi^SViabyZ8jfEk~4KA z(=E{tY;|Gk-f6I=F%?gDGC^LtF!zYJGE3cAv8M+5L9bK?`v*Za6%_~RjgzpUq~Jx7 z4gUxXK9DY`RSE&p1~MoKvxF*}Sy^3NWGmkO7$;)6a5I%=GcmY0(wN!ov{IUYj;1Re zV~@xqVQ@q_1&TQ;?!g5!Ox){O_k7UE1@|{#`4m>QG<*{MH}wI8mvnKS7Gl6mtmUYb zFcG~}Ix>nCU5im(8<9ujmCE4-6M=*U->e)|pdnc+iOL->2w5YEY(`O34fsyT%EofzFIpK(w4X!A;&)cDT)>lB#GbY@vMA{$v2{no?l z{h*82ny41sXL&eC5UDeY0taWjvA*`~H=1i$@DL1QW9fYmcHkM%;9rO)!;s1##xvPj z2R!rn39G~G!o%A;nU9xGtW@GlL_fLYrVyyPvCB*<9o(u9;(v?K4Qi)&mz=Uc!@Ht( zwH$?rz)1-c77`=b3sW0A1(uh`)vyl%rnvE*qU00`VuV1vARykVKFVYXB};bmy)jOUNap+kS-4)|o#+je-8E zeV02pcAFP<@AbLziCT~JNG6syBkeXk2s^Dh58wPP!m27j&_M-KwOFS2deLXEDzimL6#sMhjKwe+# zUFYj`#f>rV5CRcJym$iJj^-m$hG>8cvBWW?^e}os7_j-!brd~@GIo+M5nxIj5FeB| zycvAH(h63Cs0mmWrVbu!1K+Erqo}CQEcxFaNC2#M^M*5Oke#Kcw7h84lR7yu2H}b0 zx2Cy-#jBQi5Z;6~{^{~iYqBy_sXTXC_S|SZ2e{TqY;pE3VsB-LqO8+H6dgw5KJbo* zulW|xeXvtVNQ|0zUD=vdGu928evdDdjBa6Z-0L9y9Hf*GITfHkRvq`O$$YN5tHK#j zc}qqbiorhgPxd-CRPf7F3IVrANHz6gOwp-;-4k6X7k#Kvuh~gKutTAxj_WDN`Ik)1 z84DH1;ZH)_zQ7pgPG?ENEUA+F@q+z1a*1pLaIwq<`wPF+!bap`R^pPUEg6;{!Rf*) zwAUFY!c553thszNaVoPjT>3}1F6MXp1j-S2q}bF9WTykRGukO}A8wKimzI#^7RB9N zIdHUdV_7!2DUBVfddEJ#`Gb$6y~+f71IYG%r;lG?gh3kV3gcs;UT7zmR_s6msGGHH z`VD``S>E^~fyw)4T|J^UVlmKlSC%@`CulIhzg3|izR?RSKgj+YIs-h~G;1X%4GAJu z2NqZV8i5+S*9moOWXiQCa3U5sf9`|(($>fFk<}2NWYg4rg|z(pR%vl<&Ro_7^TJ6h z9mK-G2{u470W}dCB=y2ETs5*;!6y3ZHyK>;`222jeQUPP_sAaQI;p7E{W_rO*|xtY zwq-)4s{D-2O=9lbYl6l9vatujlTu~PnrwQ_Q>mCHwhln9I+togFLj9hoqAkl28(cm zkeRY56SG@a2|9)AMB+P)nWWShg)7@N$MO2zrhOo$0u6`}XP>zFYo4skdpnQsT$Fvr#tBZR?JS4p=4C(*hQF*o3y7)XVH<71d zuwrbdfwyHro}e$sqy}`oAU|_`c4Tc|DR+V7Tp>@ULFfN}PA<8L+3wRxh3A;FGHo8^ z>=8)Oafb7)Y286|=^j2JJ>-V7&s{!F(DNIC6JK^Z586qUAKkP*+i*JvJKB;+1|!T> zF`IiY=gYCNOpGc#HaU!_nHcj_5xveLLEa1?SH;iz#XK`K4GgtFq;3X9Rhcd zqdMd<2xg4n8nE}t2>&Uqg?jm3`=*c=LNleybQviEH{9Kdc%)Y|X=+V3TG6+zP4Fod zRz%T>a;pjitRTSgQQo=I zsb?gh{L(9@bQDiZvz;dnlWxq?wU1sayk#2^)I4JDemj)L52>{2Z+!c6m{>{*gubJO_gV6YSc}ca4J9YGY$Rie^ zLRmpZ$COMGcp+k-A&Ol}3COCNOjS$A%DaVs$N|6@ZqzWV;olRg_)7@*H}@r5QmX?Q z9s;q*1;*n$&ll-HMlggARSH3#QM0+KZS~x76~%NVHev6rdxzTtd9YB8n<-Nlaktr+Hkobi1eMxoHJ5h?Y4*)md7lWv9Acl|NX&mpQ$2YNl6xjH)<5j(rL zWh8g~9td`lan}=X2!II(BT9E_xD^TbFZ3qiCrZRHrR183GZIrr;!)q92&5K*2&}E1 zFA?1p5ixK3raWQB(`qFzlE)1xjOCG&Uc45Ar(wSbVfU^Ck(S!vhKQVDQ^O4%@NfB@ z;g*Ni`^xE3LA54BNWNM|V>oq!rxZ8fi{7K=FCqDTG|Y%BkVh~diNK1t(0(#37rr6V zvV6R#9TAW7ba}?VzB3tYQ~cUzyMxQ|L-`f#${L~#9C8TzsX2lhR*xZ;0LoA5xSo117w`0(zdNY^K3aP3)k94gkc21u>9Fc`Y*)z_HLpW75$Q=naIun{22J#a*Bwr<)+WCMWsX|16%@*r8T8A&84$WtF{aMudETBMhKj*Neuad zPWQdL4${1&&q=k>mPBWZ^o1p_Fa2z-_bR!f#IWZ^?$UjVASOz6_J~%z9w&|AJLqyu zsq3v7z&xG%N=;9_n*WcD`@rBvbQ*QIFY2ZI(nTfX4(Ea! zCy`jVeaBO_s(w7HT2nE*5&yZ+lZwHF_)8sXztUW0^|_NlFFPeQ7=QXMu0Xn8>D^^@ zO{N_<3eC!bpjncAc1z6G5_*y@ijoWED>60Q(-NPpv`Oa2n;30=371w9>jx%@hP{p9 z4_F+QYGBJrxQ>FvTWMm_5Ft(!OjJpEhaYJerBFkk`4-`Y%roXFnkzZi>9rjwc!#E= zA6StU%ut%U(&}zn`uJAiX|Xb|;1U}%G!4|k#mb#rdZr%GMyb%1CYUmFiTZU+!3~W> z(+6;uoR{>h{b4&PqV#IQld;>WVggb-7=+icc=3gfb;PuN-(%Y1Fxe`k7B#~q-iS)! zdb%d&NFTUPRNo!Y-J&H3Pgrl9l`=4M#WN`;ju?A&s%rlJ7}sMZ1!VQNO1TgU7=>e| z7ss7L+bcba+opAO8~&;b>?sY45b

+iMx9FUpWk#LYZF4$M2CK}5Oh1=VQ^)adZ-J+9mlhxBSLtlh5A#>@1@CqP z$^Yc4GeLoxfs?&6Ki^3^`;M>OwSeo@(+kp2@Hsul^=9V|b7k1D0eYN*F~Ho(QNt}K zp_x5f@v1e=<_S{+Ruhk`9Lq&DjjxZ+>G+t2J?nsB=r^uOxd9<4Q==@-*jnvmct@;T)qK{*0-GQyq zFjRHJ{->D&%6xEJRUbiF0wz#xDB6s8KTK#Eo|nkrzS-roE6mUqLQ)_1Boty#W5>2? zbEcbgNIC(kOn+$Q2fa4C9~{c&M0iS?NQ&CUR1}d+5g2h=Crn?^I<$c$_41llk(K$f z+LOgh^K_K`B~m|m!4^N3bSXDywE`L|XFKbw8LrQM#9Wd?i#;8reEmixj>#$ncYpO| z;fNRHK@-#k3;M*vlP+JuqY%T3_5NeTDoaGG0_X-b4=Vrp{{2foIxD*uXgBi`p&q9l z>PuKZa*7W;mW$`LXL7^SKQQ>q&Ub%H{Pd@o^an5x=KF>L99P-HwfD7fALx7Z)wq<* z{V6rRlXP$6&O&cd{vpjjTQuv`N82J2g3S&IwgVOz6LsIsezxlyiVE}D(`KJH)d{~m zEG#}lg<~^vsr||vTFFST|J`-j--0EHbilX&dODbAPNOousA^bo zbcK}23yIPoB7}M&noBBQ3ABQPyRvPEZzqpPnlfI2+w7D|LA35m7R4&2$EnO9ixP($ zZqc7VSg3|d8fW*rxIMst+l3IncNJ7@9lpyDwaXT&Z-Cg)h@|t@N@Uc@TXH!k#5!Ed z=|aLAV#ao{@#{4$#A-kqSS(Y=(xV{qmVziOCW?|Oh+zvf8*9!`;KopZ$dHO5!$O39 zZ8~bH$yfj<3w%Pyy|)9mS)%kc5N9-@8#?haI$$!Yc!G+;t>JNK-5SE*n}0OC*o|(a zP=cPMR>K;pxtA+QM3uNfl|oFE?x(^iZM(*O#U5d%k_7NBG*m@GL0&t2)-lD4pQVgg z$ITZa8&&vCrhEf-g12DRPgjv+Z(cjC-e|ZR<5NT85zM>lL0mckurZj z2xwgy*aDiBot-mXcSz|LuIAbSBSsUFHENJSnsSMJytOO43Kt_DDUZ4Ni3=en4|;w9 zXS1gN_TQ!WegVSzRloo58EAA@(h( zAhjhAQKsy#L-Kb=b}i=pqs4v7eNrn^Q0cn(v^9g`P2k|SZOC(;v_PdUwRa&YLp}Jw z2h`@{gq!39d>kB(u9p9x6!$G|sevJ;q46J{mKCQ~Y4!6Us4czirOAj8KJLaUJvxOb z|Eu=b2+VN%1WDASAAGa!Tak=&ze*~>ad_x8(h^9R=#I=xASgY$2&-K5b7=#>Yoehv z!(JjK2{_9`|9IrguVGeYYpyRG1I*8@!}U`UpG!aoBD_Lj!l1Ov%el zVqz_LyEXmtE)pXfs`%jxWxWAX=fc5_SH0XP)cUT13vUWD)r&`%kTZqYGmqQb&L5|L zt@ZaY@CC@e@!`rZjV0ws!s!eX<&{X#?z)C7CNr7yHUj`2ScyDutZ0PGPFy=q{KbMz zd3>suAC1c~5v97_-k(}AE=|k#`SlZnceI-rSiPZQbQkg}%LRh6I)UCObFFE7(oQ%5 zah$1t9w8(z_J7wym0+Y4_FJ_w#U=2vMK>*#4iocgccO!lR$W>QLc5*r<0jqc~(SZ{8UrtW{-!y<;txyZKGDF3fC?E76fAz&l>g}SNIx5241zy-FW`<$#(cu6q}@N|x4((0_kiAgbB zKLBgZ9o!s(&}#J?tfQfqEkUQsXY3W1#2L<=D4Ws81HE3z&gyzbiL*K><&wVNEJ=Q5lKmHD-O zfWehDT)n^nIoI2@*UT2yF|RE;m`}ncwdIY4FsE4fvX4U;NJa>Y=+{Y>jtmH%!~Ss! zByU$(0Tl4{oa&sh`EN4TVEl1tNL^c-ICh{>cPOkG+gG?qqp)zXb?T%CSdB}<#3VN~ zXvl@~8=2t^8$zTiR%PDk(8A`OHm}$+!C9+Zwrs_)&7@+DJ=B z36HY5TOW0y5@%2M=*`WIvg2aimZr{Vs}|_24`QlIOnDwnyx??iXQ<2cwTF#M6|iE>g>4eX(~JO zv>A0N*!Pr{<#G?5aMvBbDZj9K^@lNR%`VzX-?g1R;(|9jYVOD$BR*DtN-k?{o8A(Z!d<^>ukwQEvjZSrwk#TXaz#NSE4P_2B2~J zGpBk6rEF-8cRCuTc(6Ez5F11-bgxmQGw2j`V~G1g%8t{F27xO* zRaWSRlupR)?X85n^bS7Fxi)2ghOcZ3;wI7zmov;ciGM9ejf*WqYL64>&?b-@42kRC z@bT~5TLko6|2)Zm2KUJa>ZrSlNiSN^gUK(+fpp@f za@PyPpGcGu(?~zdpp0?{{c7PR0qw@Mg||2V$S}@te6D&EhDC5s?(bdG`8ysmmx;qc zr~kc5R0b+MEy6`+Y?!!oZ*pFmz2JeqprXGJKYzy7i-SFPR!iZqr)}33 z`sBlMOM?k;xngLgJRCR@8GfX5rXd8 zo(voK?YXPF_c!BwWE#>Mg>nc+oPtVvbVdbk_ln%e=ZZ5C>39)la37HJ8d>zV5X`a^ zg^WAMq@?$2$Qg&^o`>_~EGoyFq+#E9zI@bPtwYCnXm62@cx;Ky%-g`ufP%Z% z9gg!_mP?es?gl~FLaP1{u0d@Q&0*n!;FkO&jTAg^_Z1;;5l1*EiiFHqJxP=b?dwS2grC(5%jg%Dr7@KCq}bO<*g#738-7?Y=W7sg~DOc zJBA><nq1DPVa(rc>q*(woDfZPp-C-V{p0M-$e5CLga2$L3u24Kkp>czQ zvRgNP4?~S13>n%;G^l6NplHfn1pj|j)ZbzRR7^TfTi9F10RAJga23D6)4SjWR`x!P zu-PfGYs!v4?^?hg$X$?TU~4BPM+u-m@}e6=P1jxA1p3#1GCX5sQDpi!0o9cZ4;K2L z%qUow6^_BEFx#$b>v4O?Uow;A@9%{)3IF0CFNMDqjfuX$W7`f8fyw9XyK4m5DvcV> zknD&envv?>c)XNfW~|r^f)?qc1^a>+tWip>C#pI7LejCM5WHA$(|zWi8UBb)n$qxz zMznJe*N$?ppY}#7r3uv%*#_SdhRMA`%r;U6XFSz?`BPrZd%qNVnq~6QgEN z-c{P9;*+M_?On`&C*I8bt4xP&9g{J%8>GbZ!|H798m^ zASH~FI399d+#f*g>10QY{GS%!$VgpA=IXCKo>B34q>71ueyYMz6#4-QE~v50#&9jW zf8jwJNvQv1OL=&B==ykV=LMr4rJ$&|g!m8j8><;BdsQfgEu}`glksYtB%YnTi|5v* z`ko3hBLS=QkE{dRPfVHGiM0py)Ag2ckC0)N7mvYcdy%`^ys(MHRcZ#OQbQM#nX;Wy z1%RZ97_9{5oLunN2(?Tjjk6U#hSl%Xe)Jkg)=W~LjcA|AKj^e8j=Da}rT=*Ck}ojzl1xtuhFmGPVv4Qs$pG zImua}GYrH>+236ZF6p6nZqM9Jsi+jtp&`r(7+hck>KeBaS7b)5TZ7OVO9?15#S051 z`?z=PQ(-TzwnTi6i#>o-&tivnlTy*yT*mw7ETx3;lOIs+2m3jn# z!ka$z)(LVsjPg(_BTHGy#9gMPV`(Hy4tn&ox7M@ky8a8SZ>~$y`>-i*s;Gm6YvnL8 zOMtc|uB^P35fY*DQF3<*t%fTxV=KE;h%x95qse@UmHsg;U{BzlT$juGF@G#^e@yhN zmTwNgJh~wA{ouvvkxP&NHdU^-DO$>5f|0Na&QkHUlL#6`amWmv+=(raJU=7N)- z>OQK?CA=>`dju0uzJ%YsBSsT{1p@{J)&|!O(6Z{{j&jBGG z=^X%x?~;%3P+sL9ih%^Ef_OZ>BaUSTHFTsS$a!2uex=kUULe3!e2qPCd-x6oRqkxL{3IXQYg=icrOglZDlfg_GN6{p+TY`(-2 zPZqm*e#Kr0>>dqUv2zfOYo0G!TzroUEL~I!_D26}7g@M**7BUxPSJ*)eeL;we&*6ShhEEGLnQ4p7f7!&B&2SYJ7(`!%E-w_B<(wJj3Ewc zRs8dHc{(O=Z!IHuRn}ZWH!&rd$71k)DnIIkBBgmi(Ix{G+ltMyQ}U zE;Sqy$$Xx8P`9~+!e?NMI9h43MZ25lxA~$@!y1QEk$o+K-^ZA3Zac0 zWMn>@3;6F|K+7K;w}OT_cGWet2qGes5olP`Xd+`ilO^{rH5@Y(g3FGC)&s7TJ1+)_ zZ5IvfF;?zt{lgG08EuU^rY@MC0Z2*w3F%kpPNR!ZD4diY7+FObs%6RRE z*ccs-G3`c{j6VNwsL{9NA!NUt7Mp8s7}`~v&5v05x8m-MM^H<94pI@&PQ+8Z2A(}0 z0(bt|GVob>HDDp9yxREV!gGE(5({?+5qoyk+Wr2Y;e-wRIQ{#r3v-bLv5X7R2s6nH_8O*fD-wzH!`1-89b4&W!So0hAiWV zIWuXhd$K6z*=o~>9^RBq!7U0(0bWKafiK5K?fb=cy~AmhPiaqbCrdl!B!^iyAFQ@M z1s2_Q^A(#(>Qfb>0A*6Ip42@q;|-4-dw4p^IX*!_58No)`N;k2=+02G;w_fCxxJIq zK=<=XxA{g_psj=AnMDHznEd5D+U;tLf2Zr|G<;~sTn-DcAxkop4@Ltsk8;g$Fh-B& zCUqtt_5ywHeC-LVI-Q_3S$c)N_W$0x`&HQ7`%)^1l88_s8o&D$`aW zb?C&`I5C)Vjz&D=jnAvqnLwBSJB$K7uZ4@~IU&ffr|?(A?#!D28|-cL2$n`b#_St& zX^LzOPl8@b;G(j`kZ!^g*EilLKEbo zz*+mGdA$(OJaON`KEUOVfj+SwiTtq^O5)ulyYL_VB54KaVba;>mG!e5uGi;Zp2uvA(~Dta zS^;fN+`(@w#_}+c7>|7SA^X0g`$-rMaQzsa41cIjo}r@^Y;nLm7vf2f;B+29 zz!L(be6Yf2el%hDU9ZyD-u|E7`K_lp-K%cQ+@F8-C)j4^Nc-_K z$wtuxRMvhOY9w%1FkUoJbFP1vCrfdXg4lTh$EqaQ{KB&xx)Pldw9mUFvK}`Y-n#-N zA~F;}m%ZTJpSRw^AAT?@GUSTUAiXAx6__zL5JMWfYx~w+kW7VTNLtUQ=K-@HnR1kO zBW-1DRxE(zf(i-w#*T))Kc&ivHRsv-{f6)RB+qjSYxtrLsA|K_OZx0T-`_S*ti|4l z%@zF=$^5SMG-@=n!9CI36*$br?jd|fQ`+L6dtqRGt?FRW4xdWyMYANAwieOEUavg1 z8f@4@|ACDdTC8SG^BqJ1w{^D{J}bhe8vW%PQE~9ZA}kO?1&)!0yqIVzK?;scT2L}d zvH3r!4)G(?FDjqZMT09(>0715s!xTH#uCOdtq5cGx2x7EZHRR#Fx9>OhKo*zyqKBE^Q`;rEe0e4Yu%-?pwqx?{`!i?P)QwChnfz&1o1tGU8Pm(O@Oo^Is4!S@gN__UqD zdx^T+%y|$7B75di)H6bYf!C`W@~xh#p>gXICt)#I{m@aQD-0Oog7t zyKr_+ByLry7IQ6xx~!Tfl!EvgSZEL4Jy)rA=bBjpW2aitW?3=?Ewq4ffuxN1^s z8-}9bNN6-ORw0|TTgNmm`%vLL9!k9HWgTPCl8*n|8>*6sgaLr=ptxd(t{tG#+X*Bd zj|rZyCZ-FT4;-)OudWhWputqX#qs(vOdUZ0$8qNF0INnvqnFh-*|iB}#W-J9A%Ff= zu22LYDYDN0&KdG65Av(={H1BZBp!$r`L(s^=wkO2U%%fj0|clZ;>I4gy@|{5$&#We`8+ zA{mf8^i6d4Br+uf`IpIhu$g-Alla3j^jqLNLFiI;CPWgxZToy2TEIK7PK)1l0E#8= zWR$_a#}mu@saVW_gI$x=yPcW))6^etkk3?wb{C`Ao^}Q8m^sXQ`7L^QY=W&}rSO*h zGbJgQ%vMrG$`7SQ(t|OsqMgVGBbioyHLBQE`6qda#5Ut;3Q7J}`oq71v?d{flkN28 z>Jvw>+0ABpo>3!z6Z)RPpHr05wGUfDs`Qty5t*B|+hby0l%INz`nS4@CCH|&e!TM^g|<#k2iILhW4nES$KOx) z1t*quU)4_D5IDy5)Kn>6d}3`XcP(H2i5JSFtoH-s9ZlA!tw2iF>I2cR%c(B?T7A4_ zp^1uMyjiEttDG3yP(9;7DF*iMX2fZ_klVlo3FEo!~ z;?DGVwawiizpJDGwp3zyi{=ZQ^Z(|d5&MWPGMxG2H>PdTx$ zvRpqT^4I&JHmtv$@0KBq;LbZKw*E5-Nbl}WjPCATyYm6CzTp_u3p7!W}(6FCUga{zVBzsJM3;>|H zB3aQKooI*=hcajDNEJ~!QM{I{G&?t%dGWBw?U>nb&>G1-c`yEXe{Y`UF6Y6SrYHSn zF?oq{|0I_0o1kg>TkRXEHJeP-yGl(?6g+o18C zt+VVVsH(F)P87HtzY>eKV~+?mY@D^njHHZpfzCx76;d-x;rq6qaxDT#G?A9@U}ZPa z5-7BcK5KVyRSs31SB=p3oYphnLo$SH!9F)1buUa`-6Rg%+31pSYrnr3ES}&WcA6M# zMZvD0~F}ff6C5HQF(_5KFSCsSG zpDxh7@Tv2He#8;2s8Z*?@~|=#1De>y9REZ&y7hV}-fNx<03^-nnkMe$1(VIq%?W0! zczW{w4Qs0&Gs>;6FHe{#Z~#&UJ+%ZAqeVjtd!POu1~?moE;@EG_UqIoc}mxpHuaFaG&HrB#5v0iB7Q}fjF z#)*)1nT@h<>=zBf18qqeGEd2)Q-k4eqnhacA5~u&6lb(-JHX)X?he7-od6-Y6Wkqw zyK8U>NpMJj;1FB~hv328-QD4RoO|xAdQ~$;&9520-Fx-w)vNcODPoJWIhD95LKBX< zhgtSb6eh_!`5Y+NTi-&@@%nu(ex48PMjbF}MV`EhQN_o>qMUr}?-VgPR$JmjdBWAA z{%&`rM^O0}&-sJ8TtIh!x151_cj#8<>wV$=huA;pgE<59sXAYOp+Wd4@8g&+NBY&} zMXfxBC*kqcOOCH7UVDl>9J^i|f9s2tGt6-dD9Rz1J}BoNCKD%>A?!TCBfr%;z1txe zrn*;?kG3(Y`%~i+6Vd4bH?E2iw@~5;P#-hMsBxeh+uCs++eA@h2uStEtBX?cLmssP zW(~^;4y31>ZghR)0Hcr0S;Mka#pb61Y3ZiCDrBv-bZA%@&_5BFV`!D(oR9|>8vP_m zIjOD32Jw8{qsyFE;ed;joXpo-QqQH}P-zexpmM_%%_#;tR9K8o-&v!EO#qcLuSy2gvsK6=8cw&0VV z`jQ8WqA5a;*_Y|<@34k4?8)0zli>QfM7<-bv9Q$zgT50js_}28rU`EYRO%P;* z*pGjP0cu6X_#HGFj-s4_5uX2+8my>_J_^X|dRv?QB%}S1AR^eP?VL_lRu%&>(&edb zlb_ESElr8IUbCqw$wFQM@x!bMtkk&!V}hB!q=E*Dr`g9bAr%(0zR$+*$~Gs|tqj=o z)T)4aSSSd%#dsZH-bt%yYcRQgsI`aympjm;zZegOfj_IO^gn*;aB4Z`6~0|^Wnf`3 znA)$etzG+-ZYU`)j|hUC-kJd`4pc**7y+$&XtChS`84!7vZ8_9Gru!{CD4HX)A@PT z54DbO$xXG8ZF}QWxBf85P0B%LW|Q+~g%sM_V z$D6pjk1kHTBljzD0n=|NBt1)-vsON~@w1%2$B(2ga;+;8k}?v7y_(U#Ym16~+Yk}r z6sBMM{ttdX;*?zaLi}24p!E(S@e_?!jKANHT1L!rBk$decTLr;nQmDJP%VoF@^^dG zkIn6nOZf0{9OV?1+ZW~>`wZ;*(@kSDah8K}4n%(6{Q8P>nKPJz3REeq=cbvDi)lN& z{OWwkk*lYYFJ}}1O75O`fkg~?t5&bksh^Dx*k$wpEh$JxygK+&a}qi8gSyILjf6sU z@JsN|O%m@S+|`FZzVlbtsyRU7I%Ywp}!(1PDpP4ILv6RK0ukQO_vP69Gavqm<8%_VSOG5{qHq4vp zO10ZUk>5l;YeZmli}f`H9`NCdA&j`6GZn~l-Qv^WbETq!L|_VZ6Ao|V%*@wF_#nb> z!t*=5#rHy^=G-q{6x?PjD_cl^^8ms9*G7x$%E>vap&+Fm9cP=1h^FRdo1B4&DDElN zJEM}^Gb!ErTd$bSM|2oVB~GOaIn z2LIzmZVtqu%teHfgD6zLQ&T`eMawTOz@D(i(c0*PXGx;Xy#lNjgz*rh@L=e9$Czg+ z9nS~Nx$CpC-e|(Ro!@^doFd)h;D@D|pEgk?@59}+mK|nZ7hr14ml0ab*SUeZ!c5L= zj#cApqOM6;Vj+exY(T#7AFDC4tE(#zNJFz1*n2AK>VJM-o+TytfpZ?Fiq7sf&oASQ zULR)9^*gTh?;!A^5L{r()%uBq^>)bd=%xjtZ{P=#zu)PEr)rLxsVM3Ep~=(O>P@Qf zC1&hZ=zF_s=$;540?6xljzbX~;#K+QOID+FiC(?QWW^(^t#jZ%Q;oG#(fRt~%cs9d z9!u)J1`C^`ZVv;h8Z$3OOBk0yo|p}31;jj}knLl@%zja0auHdJ&e zyTG5#==QrgAA|X&0MDvN0RF$0dsAQ2oB?y+!@dpS2`+5(!nqqiLi-|LwW9=ISN1?Q zmW>**n5C8Rn$`r&rvol`vQ0D@Ozn?olPIJ-Fh%2S?O2?P6wRqsRH_WD^kHh>>ONCP zgeMgTKbQ(Jl>&EcTm;SuUEUR!w7dq6X7QvrNO~X)29?#oV$eQFh4kj*!j{%6liNYv z+Uj>c2tVlPLrOI5WU`CC=;M}Xu`o$Ud^p4cCXSPWip6#4C@BwVQXFP{Pw$^y=qZO3 zIpr-mJuTZ$XYD9lcUN4;?z3G_hDB~&MXijNEqr_gLAfjP&BwVvqG_aVyg;g8uhhHJ z&+(}9Bhqi69x`4@_PWV@9Yg&a&$x-%B3q*lnN`Nzx8J|zc<@c&gzSPOp z+;N{!(a|pGjpU0)+BZwDym9I)Dr5H^9#$~S`Xim9p$jsn<%s6T>@9ZwGi?%7;20%z zr>`3iw4I1|Ml1I=t52i?5%(W}A8lh_=WhL{A&lkus|UT|{JpMgYDrTf#S0hQBV6z&sIjH?#aj=^O{iv@H6;Kt*O=>-af^zk?v@N zi~DuwMmDZb^w1-36BK*O|6BLKO24R){Ly@|XS^mwQ`i;m3WJOEh&#UA(*DMhc zu{ir8p+Vn8Siv7P}*HJvD0as8;IG7TC^Gx=tz=dIWu6mCO>`?@dTJh*;ylu zva!(bX#Fi>rH_%xZ?$)bQ1~ai>NSkLm0M4xZ@hSSYi6QT2q3&a`{#2C4 z5E!$vN}+=1S&NLvU+C&Lw>2Kz+k#I`O%3eEj#R!+9Yvyz@DT|g=o|zdk46s+x-e+4 z!CX&E3-6?9H+$fjV%+xp=+D?j408nt)OtB8#P9#W-4{>#0apeCEP+uyB*=h{$wKWl zYPxF2GKzFxu7L~26lia_!+OW~BO(9$WK!}sf{Ux7IPyrF&3-6Qb{N&(>ctEET+{9( zYkz>L^*8#Dy|v`4zmR61SDe3xBIfyiGcYShunRyZt?WBaXbUMiZEIhBK6&oepEH=5 z4el6Ofle5LZJUro7EFr3Lww3a7d$3ymLL#NocE_O))9}p6Ss37(2E77Soa<|6GN6IM8efA+$qAuPl&Zx`zb)#{+ z2r6g8gfj%I=WUC|6qzSdBVkDgNY1yVkTosmGx`eh*is+vbA4r;|Mdb~eRrPnv?A&a zIB4aL;>3gI2N`QdReoP@J<)}(ZR@O>SN3-+EblvfrZXbN3>Dvr<*T(dBF&;Rq-Iy# zTVfr+s{aA=;E%Z4i;^z z!E~{a)!}%Z%13PJPa6?^XFoGF&a|`bZStMN(6P??nW2$y3TL6#P+;Gc5THX=Q;@y~ zC&7qG6oXZ9s?GZ2D{jReCK3`eGf5_dS#)c_;o;#7%<6rp*qophqKXv05CK@S8J=N9=&{DE|(z zUg4hM!_RqADMOM>D0kkVeW- z8|iw6I1c{QJSytofqVs#JoV@Zn)J#|FTD`5fk#5>A_?wxCySatkp$@nn13$SB=HblIPOd|I zg;nX7PHCnM!>eb;o}+Im2k&b0G%pdNZxN|jZ6b4hrO0@WO&o_mtMGTBBi?OZ_k*qK zjp;%^kD`a(w^34Q=r`RZa(rFe#ZGcfe2b8%@EVU{|uV4t+)1o)29Jp&@%-oNli zAj62>pdhLK(yC`8BQ&qF^7?r26K0`9SkL(zCfv+1dzoMQ>ag2EU%398_;&&U8n7;V zUe`g#VF;*oz8;>@w@}8j_-MmIvr(6$q|H$g{|$%{t(p=FBlt+&2$iqh3uE5C34Hb; zRA{V6yWghU@pEJIjxL^!i1Htp!>E3ev4)36xjRKLFRethQ=MC=Xayc6SOnCk^{C_^ z;=HO%HP8-RmPc})Sf0>lDXF%!X%lOI$raPU7AZUHIOszvmU?Hj!K_`Zofq|iXtCC5 z=UZF49hkSsHA5DUqws>_sGtvRD4ZPB*pEyYWXvv|h5cGe|0C%qwzH-L(brGy;PiLx zVAN|==aywTo(vP-3`ztP-t;K=j^fU9-e%>463LeHY0yFV25y*G@8_;LF=mYdMP|x$VL<#IM+BnJSL(w#VmVB) z6aZZ?&CmoX{iYztV?amY6ZZ~kC!DB`<&!ARPVJFi@_$BmzPw=W_RZ&}Ha_58nmd4^ zLE~?ICez|J6Ra10V6Sg|rJNDH6xI_GEdKYDcw0`(zuSCRKvh{o(o9(_hmhUydN`CT z{kVMIXfP$W85^@er!f^E8yp1!)_8MoXZOMWb&}ZQX*JNQ(;O!vl~v#j8;nW&?&{A+ zR4V;LiW^j#ujtbR;x=Nt*SGUj3?Td58IY6}WmlwTg5V-HxWbTYa%!;oy z_=i2JEeZPd^Wvj*s@=zo`KD+Ie_k_2`ys61X##|%@r+ZB7AzrHQeEY(P< z93EEO+TPA9DM1R_=u(Cb2mzKk%DYVEQ=wKVgEF%}8#7K3`{!(`D9RdObK5sQvi^*n z`nf(Kzx;CZ%;u6i56dkWTo2h8IK(pwp_Rd3D>M?5bbMMK|DpJ;bpy|Q_y;kM_YNh_ zSqghLMbrrx>l8*j@%h8lUpI<}jPAUI%`a{gkHwn%r$c=^wkzrlRdo7wY7trUc}JaKo}<}< z+ASNGAUmi7#audHkSQb9LLDW(T#)54AR%JejcBmE&idmB>5V|6Hh7m6K^Ep4g>*$# zrWCbL;@qcDbA4Q_1$%anIZC_QVq!kx`c_7eP=**GBz=T6)4o)uLOv3u08g-QEzvx4 z|15WfRx-xZ9TUEhhROY1j4I0#JV@dzd-DMX=Ry1<_NlA<4sbT&UVx?@-_*b9P>Kz3 zU%acsh1BO6@6%8ld3B=Shg{(lhC&ohD7Ji9XYX-qK8)_|nqkIHUg?CWAQ}aD&kQMC22yI`N`S|j^_}{10=-`cqHi+{ zAiwdhyt|4_#ATt`EmAKOX5ba6XgM(T$@~dCU)Ov!f9qG4 z(gXX-t*e9Wl?Gm?MxjQ7;cpm95fR7p_tGhHBUc111|yKcjD{JCMBE6|w{(CHj~ z53sSL7$e)5G6QHu8>6CF&S(->;-}UU$>l`*X^}~Rchrgtk92+M$I?6dm5Jt+;WLLJ zE7S2uDUT-3cWic%44Yo1#J-0@RV@{ykHn-wLd=Nr% zx7k1}X)TRmd6Wr$nz)uyTibu{A>L~zA^su(23ugoau`6JbK|17_Aopj=9 zC?70QO6Vat_?uf;9)t2YL(L5h6P|kIbSn;h!Fi=g*1Qll5S8lIAK1lRk+=}1RXYKk z+fzxf7@d8DsW^xM8)_qZ)aA#C*@rCF$Co~*ukifY2yDOF)0?!38^GSJu#opEbkM2c z0+laXHVF8545iqqYS_^%2YOd=gz_t5L{~aP1kEI@ilcbuvm_GRjoxeTz$*wUkz3`dMu)J z%DKOVT%MDGFqv>KK$*}e6J(#>88SbuqoR`gySSKQv8=dwHo(!CkOs_U7fKOTrLL(t zeeEeJ34L*Kp^+x`rT5=;!?f)$B2H!*Bd%y$P=a!9s1Cj=?nGVL}o^l!|HP zv}YX_jh8{vUp;8OpBpAI88MX)#^J>vp;F%u5!90vWn{-`auK5+anvlvGa2>;SaORjw#s$qXzuN)^Wfd` z&_}PUqVE#qQb6@Cfl)${IPS}^9~Od@>CVI_MTl>RAfsVVxHgBM7nR|QocWBH{UI+Z z&ny`0${;wGz@K!LCBU?i1#Z!eB3MXWV&zDsZ<^P=H08u$Nm!hG5y_ahOK9J89--eH z7N{RnNGiFW9R6C*l9Ydaet&zse@(w8M-tvwhWOV2i`epZi)3j(jAoyL0L3^K)*S@~ zK84oWfE2?VvG0a%&)}HdgInsimE6<3SzX_1vxr_ZB^@9+pceXTEB*D2DpD$m)1cG&;2l4% zdTlnal18!YgO3x%A8u2}3$FIVHdBN-qXDtt)D=?{2M@h?a+i<)iyO zHn`Kt5CACv6Ez|TA-1cfI1tO8oEB05P;fV%m1qe0|AAvaX!bn3jI+yZ1(wLToZL#a zM&G3X=m)G^Qfj)`o>`IQ59g7oE7sZPC_X6p5WHrSq7?ghG!wsCMQ-2%GS?avvvL)8 zGFKK@4b{*sfQG2FSl#9({F(7O?}K+}ywM+oIXQG5eEV_I+$Pt5986Nv^0*;4l3}vK z`uqczaG|7Du+Mh6QCZs1jmW{_qr~Id-^dTqE*HnOAss0GuuntJB>GsP{&YVM7GwT| zylA!uxTWVj9Q8n3C^{N^yD&o=SDK036I^$y+um-k!RCn2pYpNO=T6uulvn40&X{h> zf*Sa9(!VzePl>E2rzc__hnI<%_#Gr)5Lz}N@oz8HAtaZi7v$V4l7b9vF2TVDEU4mF zC};LKIK@fE!A*=t6bY<6*E%WcW+*g-Pa!kMVH7@{emPvLF9C?Mtz!h{acHEB->r#x zh|0giI@87x)t-sXtk<9bHLIbMV$-oF4-6}H!3)1^jwrZ%PCH{vz_U^K2)~FZmX>CP zK8hgH#=}`^fqW}g0G|3Q?0bjg@y}bla@oKnsLHRaFz}Ph-)d}ny|KXtFGw!B>8(a$ zkeC8%^JgCjVJC^2#sV{@^g9pDW{J1thY*6m)7w7F3DQ>QkMgp3cHk?OYF&+{f$7h{ z#SD~{Tzl^O1wH5^GjJODMoA{!ue~nPw9T4dIFWWufi=(lNRSEYfY?J&x&A!ho`?Yl zO-41J!o&xBadEb8#J*dv&axhegC(S~Uo1;yHWejXa{TlqQ|oCV_ma`LG0yjl43cYU zP<>4A_82MHkU2Xh6ryvQujyk9@zxr3j<_uLm~l>*S*PT_SFX%Rxh&rDaah83(XSa1 z8b7L2+A?Q6r~Y>RCyp5Pt)PN7kFs}5C*6K)j4R@5^cXswGKvgD2Fdy0jYDBw%l_+U zzHt}$O>(%+f_o|${g@AkGia>xi6VyLIA-HKLC+?o`OIw#;3 z6O-6jtRH_T<&=!A3sE=0&a1;BGPI_{Q$Nj%1 zMb|w5z#bKSXD18~dh}CzqqzCVfzW4?3|*7!QF^mbI*WFT>MBb+-J0o)y!qhl zO*Ae43T&&2M`n8v9M0JgP#9*fW{b;;B}mhv1%mZ_D3{ zVPb72{bParOl!OU!C#yejRKu)d5qU-89F zuh@g)W5F~PWoJ}RkF>e@E+q3u$W<)L&%#0~Mghc~T5cM;AGwqJ6n8H3Y;wHD5+0l$ zS`B>;2Cll$w;tHXmvh;}5OYGCks^-54bat#c5*#7b-Whp3GM3*lS-CoIdP#QbSJ7F=iUbkSfY{^fTydjOEzaLkTYCK*$Z3^XIENL6 zG=6w(92@-DY;Ixks|5U%6YyX&yUJxbocqR$>?2+yMS%6LhH%~m@)z>*N_boA$ZE=B zAn~zvATdTqAB^oK(HZ|O4wiXxAj_?;6i`$!rQ%)k$9H_Vrshy}0(8JXC++0&-l48r z?Iv1U9TktDGZz`;?E6;U4U1G7L_HKEh!MMmnwN+`WV8%yzP+T$M%nkO=@<1hZ{}~D zh&}}z`QB?x*>|oNh6Y3A2`H zfi^18I5wSy2u%P(U}hW(WsJd1gh0f?O$4PU5tNH*tSA@6NC`z02ZxA=$WBYwWy}uA zw3Jm)KuS)=O8S&G*VA)j(+ITlG~ErG%cG3Y!_R)w#l=L3uEyubcR|W7_1Xef1o-yJ(1>!;!$ zt!+S0!64Ae(o)R=Q(Bo<6#-BVRNsM4)LdT5RHPp>#L;xX6QOS~(fagvZ=j5Z`Q$-e z7V1YpwrKnZ*rjBut<&D*eB>%XS2$X_w9%N=|2OBS*y7IL;ciYeih=?kV0YgO zqkmF6J#+58DX(%|C91#j^v_d7(y8W#r7IY(D|Z!=z>rQ=jcFfUV1_E|AJ44P&ad2C zjz3e%WD5;cz1Bl97@oicT!j~13*V$Xzf!(KEC!zrMWyD z#2-3f<`@=14+t6Hg~|;LgAKOkA^L_D%T9nYZp*#i#HYlQ&reV2q+Q?RDD!z0jHIdo zepEXWI=-c=Zo@QV`kT0QT*=dZgjq**4RloUKIF1CeMgK_b~SsjyD0v? zo%9#pu2K$LPx%F>#uFAqJw3bzh)N{g5QjFp6u7wr@RO=^pt3(L!vBQE7d(~&tTU&T zRzlAe?;@rPO>OPSzP>+~EqH8(ifR98Hl26EU3KJZ|C|GFo9HoWVoKcY*5o7Scl&o> zN8%UTYf3*nb8w*Y&*cjH9riu|N(R6MCKe_z)7-xT`tci*EP&O=`!o3j-!cetiqal^ zEob(UdYlu{6t(Pjrqo5u_+EqMIHMI-JELCIz5rxfywe!BB=Cj18 za$|!LE5_13O1wa3p*q}8eXXu>d(NM+P5}*a#6B#r^X3Z(50z3RF#k5m zd2!`vcL{HmptpoF-a!XCv!7f`Z1IFhcK*5CRtImbm3EKWJP;FdM47#4L@N6kwbUVC zVed1;rqB*TZJpfh_qaVFn*!IG7@pYOhKGIZQjXd*Lg?}Z!h>khaHt0t{sq_EXz5;l zq4lsRRi)_+w2#B?Ps%l_FI;A2hY?dg7c?vLdLhhT4Z^3`m1aDBTb+Gd+4tPp_fW@e zrI@#7?ZAFcPYh25&A6aS3qeT(vm4;hn(kh2VAS|J3_ zjwSQ|Co40pcOkPKJ@^8{RK?}xGD=EHJ(qRfm-8-1`;$dxCreHL?LR%@P@nyqN`fG5 zuRo}ym(xczn#xZ+?G3pVV6zhk*9K&6@hD>6lRKSUsv3IWIT{AwX8UbV7c=`PO+W+v z=b_yACKD|@){MlNQ&F_a*htX|>G0+9X3h`!I9O1D$pW~OyJOw!ZI2r2^&yNYl2m^y zsL4`)_=-9|sXhLk4>F*-02dbgQ#FE24=l44&YVry%8EQrYeJUA%fno|-0X)U? z6*mhT0@N?fsu7Tr^Fwximy<7NY+sl8p|`z|VvO4xa(!uo3JAkoN{I4 zMxC_uccU0Hry_!HFlyZcu{9N*j}`^wEvmX6#Y^?WnqRc8CJVc!wVs>K*!st{SFO5###QR>F9&YZp|t)g`9nZiQBZq&)TW`Raoa3Dnp^kvPT26 zjJ)qYf)W^%GB*g4&+Qh9pPz6tl?#JV$lq9s1mc~54pKN1lQD|DhFH#*AaQ`It02D3 z;IO-FNT;SVGZTtjjZ57(%QpLUJbR;z; zF*?<`U47gvpWK)elk*U+VUjFC$=Bi;MPu&)4udt8-b$*;`V@+Ln&Ae0!b%kS`}?!d zwf5Yy*WRWAf9nCsO_DUDxeX)hlTnG}Hz4Jx)^7bX`L*uSROPw*_}tv9|M{QEpk~C= z(Xmo$n)Wq+d!OFK`@0-_t)8bW*602C`Ela7_G76_TbqW4+>sO4iOSN${fbq)I#3{v z(r~B%BNRx*C-<~uw0=SGXrZ1oQfxW$OFD~;kJ3%mf#qly>|D!bPRSlj-j6*@q%Dk_ z{&lq$D?TNAZ`l0MPiJq+?10Y|E;UTdKs^XTbj4Q2^VkQY1=lFXMjXsJ=f8c%$9hf` z6yE*ry;UYOX$RtqHjUpz1B=f2$zPBKgN!l!soI_Ax)GwxKnGKl;9lhsF{ZbA7?9GK zudojtT>e=>*Ii43iu|rghrQ+w0v7aaf;_7JDGP`~JM)AXonOe@@x9KGMEK~DQM9ky zj{Q$|1Bwb50wy4J>ToVI!jT4kO1TEn%YccA##&(?mOZ+?I@`0WgJ~ zAaUvfjv_x&LMJP$J6v%%8E;S}r$?Z2!?*s9i28`f?zC75+5U}_IR2&%p6FDC!qypK z!G3s-B@xAd9 zohpvn-jCy3flL&YZ)##-Cw^E~KHl&Lt<|9Drc#6% z3wX6nM1AN8Wbv>M5ZKse(yQ?rF!d70Ts@t8LINx^&w;9eR-!W%4@wCRGkU~jJRj#Y zwb1P1P%ZjPeGncO{Y)Q&NuB|f>N!Ak{QQL>TLd#4`$8o#BfbmKTO{l;s|wZ=PIyI2 zKeBx6i4ZxRh4d@&>(%|Gin{8A)pSS_-F#+D_|VsqLb1Qef)%&`=ME`B_xOh|Ac4_y z#t=@$YCyVX*fHnXVGxtuwP6Yx%8fE-fjXI-jWrg?4xySdBFiNi@;8H999Oqr(v43$ zJM}|n$X^WxHxjR}>iWUp6kUugn_8c`zepO9#lZY*3kY(pydYf^dJPAmxiNd{R&t~O zRuq{a!CFbuu1&zTL)LKjyTEIoOBvRhElcU?S37Wor(j^-sXIP&!aA&AU~U?`pv=>+ zZ;xMUoE_ZIfcpCz=cuw?r$^;6)X|NE+-L~Y#2&R|7-Ji z<-tT@S9Jkvq`xj4%^>|!da;QIxzsvmvfd#F(DlJuv^QQ0*g$9)uCAP>6!xoG@j@`h z;SZsZj2N#v_1m#&gZb2)nvd!8mMK>+It9+SDAGcH1z^=Jq(TKae)&ZA#Q?~S1XYNxe@Lnsa68lHvGQ1J+pl$Lj|-^=}H8D1n?6m z(QdH+i8ft!%D`W3eb^*=2!KSGpt{0SwcD?mZaSa;sS5j=CLCLAYgWxHIG=g(l=1ND z*f}sTSBA7L@ADn!AzTIjaeM$m%~*-o#AIDs=kxo{Qw1KoPjRKv0=RiZt>^ z!T?(@J{n^WU%>aH5=N2cpUvRyF@k zU+n^!;g_x(xh<1hw7bEZlH#bk=Mq$e2LhK53x@|Uj~s*WBrw$;Gs3E{g0=DX*hWe* zy>I>3j*!4#K{@Ht6gf#JT-uKZi>vovFugbj#Gi}ogT~Oo)@D*7R#we<6&<)x3s;5g z4PTN$v)@|SYp{Pr$;Igogt*ntTt)tFihq~&wOpL<+Vx8t>|4at*i}jdNQi>$VhGpAXPx7fw0s0%RFQe7H~E(H;=d!uV6E(p3PH$WXs0uUz6pz{U7>b{WnQlvAsS2U%*=&7k01(f8H!Gf znybRGoJfFZlQ0W$Z&-ZS;K#;CpHzK5D|!sUOG_uML=s%?ijLWQzJUGn5P;YqqlVpy3j3{CmimK*FzeLxMe=(`bU*)`F z+aOmU8Pb>%g+VL`K5H&1ts9%8b`G#ZSk$?_j4-WIo;F;iHb%k> zO<6|fA@^SGk=Xdhp3}WO$k&ec7}zgtz4wn$TA&UjENgRE-m|$ye zBA^;WAc7*2fax2TnyZ{eX!KT~i=Q(n-aCtzw(M*IR7;Dagdx%Dc5tF8BLhKt(u^CC z855fLDWV@b%g~cbhWg#~x!|N|3=zPQTHu<9u)_-3P9u-n@Whd1Xj%dFE?X#r!V5G9 zx_ZS4rnCTh!$)`Ln^)=zy=TGndU7?xHV3v(8+jPTDMF{M*lew5(DCGAXrRV}l2Yfr z@jOiTj@4#&X2XE{?TTB^B^UUek&O3g1)L-z>%oW6yGAblPQ9sf4M=jy*(;qa|B@F%ov`XI40$3!Q>|xn8OqSRWBF$JlmB@@XVeX&PG`tYR7CpB48$AMkNTq+$O%yUP>gr&%#uH6Hu* zl=svv0M8F^j^<35h&$1K(QfzNG?9HYX-X?$BF#eX$Ql}XZQ`$h(|Od<{Uq=69~Lx! zA(?oJs6>h;ZprR;a?S~Fa=At^Qv{q4ua}MV<=D%;SLTwvb1oDRIdd)9@>6(na0w2)yf!o%AI?3x(g#_uJmvB$Bjb;lc z&yC+5sE%E6K#gRQ)1a>MCi~3|ealWMS2!2lB~b%|pN=)t=3d8PfL;+o2m#>?6hUNX zOK{~asdj$X=Q()Q_>b+IGpJ@Ho3;#8yUCO!LQ9wW{=0@?3)tXJk_M!|b6<#L`m(zA zR*E~fD3ksB(0b?nX~tFsRLvOxcNVpCxejJ-E48}=l3RAm-XU$fvp3I53Ei@pVYFC( zFc}Qtr?2PJ`0!^+gS^~z^}p*;GF=C1P*kzjZ@kvrn%0~JUE(qClnTj~)`O0m(KfdC zF8Mt9JF{2;)9=zIEWS+6qA0w)dW43ZoO8W#*|lq|*bQRxf&=hKYiKqLJkrlL&8%Wy zKWqSPdC8Ei83gxNy~5w~&xqJr2|+!N)JOtn{amr$P})mmS-X+Wen+?pxekX=qQ z&Sy0`<3C9!+79nNHDbK+0GBb5TM!7CK92mr+5`qhN5zbJGrB!i(8ZoG0I#iQOV6X_ zA3~@0d&_aI5YhQL%u?PbP$#eP*a;f(29c@^=OY0;_hOI+7ln0x3wxXr zg;>+8zoK65{Ofx@mKLPLY=1+Z-a&>;Vm!bhB7EgS%XD;7TxKbs3b8=L+xWFZk2fk^ z@e9;a@9;jZj%w>(X>72scj#+`VxoaM$DOdFj}zNhmT%G9$d)qh+D4ZhPLLJRM%Zm( zR(xUB*(!RQy*SxV56^ZkUZluI?6yA2jc&HU=fqBB$i4p&6bH-#Z zg_dfvbL6oBxK`LVr<^3p%*P4iR#rXzLC0&m8qgWMI?OX+vdFch1Lf z>%(#$4CP(Ikz8(a_9G@lb!0n^w=W%VCI%!>M5%lVqnHYOVA*kaMQrpF?WN`fkiY#L zWQ}6l@mn#uA(lAfG(Yk8eR9kZ)X4yiL_yx_>iI{Opv;Sl&t7eYdv1a;Q89P-V}bFN z9WO+0SpX!{W2B=K1;}&ofWnV(?oZLrH6|STHhFBY=q5;N82Ie2m?N6SB=w7%?^cC* zB8KTL892{jL~!#-=;F1dc74F0?sorU1w!Q!x8qV_r))-^+#&SJ34`2LrQ;gp51WB)cAT@QSiyINVHB(*Coe_uf>Msxp7G?0uAcNDUcb-JlDpO^Sk z0MLGS-hBf4LnXGGV#)tMi?jpM#lXbAo&gq&1!LT}j?_;x>)y4@`RKihQ@PBX1p}vC-LoByD9y7V47w->dF!deRcrmPoQBAJMd6Tk{PplHmYxVoOI;hEYTX z%~cH=iGC-}dZnz6_gji$z@>S4oyjycTi%s;ldb7*J|dU%nqS{K%sJxI{G%^IBaKVT zi8Y7@cQkMmzlZJPXI9#-2WYQe#>cW39CT+D&$Z*`cS{(}0P*>smo$%3!p!r>w3}pGECJh_s zp)y734)LDIbA{Rf?RWA$XXFmXN{;6Yym<<^IFl*F#tePHqre`eq|?8zk@xFqHnDdo zC_80x13bzT5o-Gv17M7Vqf5pTfEBemm_{B8_?%$?`U(=av0+5K18zEt=4gF5C}m(^ zfS4tSz=7UQ*A*g6M5hOqf^<)+fc;e3nnsD|@jhOjfH?qR?^zZE;8&PcX;DPa1;ib+O9*rils6 zW#|BLmv!YC^!u|zyg+0lnYr-5h2v5a#W4iiRbX{HY@FAAg2@jUH2tn5Hb=%3uY1PVIx%C;LKKpJ>2 zp)CL$U$aba=qHXz`Ig=X;25Z0@{Gf&7FM?&Gbo}E5|fR4zUC~&wgNjqEYWODB10cK zIWpj`)|JZF$V}IVXE>!0T;~)#A4F_v0b5tu&Ad4%Qe6j1pFKuw3F;7P`ubFb2#J(T zo+n9mQ}7^{9z1EclmJ8k9RQ)d&R>W_@HRc0A7Kbm?>a8|?aDcOKKbMdZz(+vQg1ot zm@A329CUTfJ)9)=oCUm3JQV%!ZpElp3LjBn{d^l}Mf1guo5%wd#-~w*UufZcBA&EZ zz#uvw`AcGwsKWHNNvw&U4|D*x01TVoWRMn{cW?yksEJ!0Rognw@Q5Zm{~`neb2{SP z_P)zVPq5Z0%VtJfzD68Z3Z)Y9$L9rF_0d4&kC8c+qs{JX|7df!Nj8vnY!!`foGL8x z4;_Q7dTnsZK3P4KXYvFRf0K1z&;{1+QsxH-AB1?ngoH1OHzO`!sh4jX#1E=>KhzSw znKQ!x|1kco6>t_xzE)CN(Z$lR6vCok?*?4>mMIY0Paa5;Aoju!g4pw#9ZdZm7#RsS ztbDJpPh6(O98W3)S2=sgcMQ0#h40ov8=r3$8OK$-fC1ir&n)xvm;jVX6@3j3u+#hW)Hr5y;{u~LXtt}0DQM`Q zGSO358z0ZJcQS9&3HE6K;^e8j)2fhKRWP0BU!FhVl}RNR zM&vzpl>(_HM|AgPwKDA|au$ut$J%s`wmG;7z)#*pa~7plDj0`~GbzaAJ%)80b}r#b z0W@ec8dmQ*#VU20jorsN%RfmlfqrJNRq&8-dZkQpU0uSR;qOwasvObL(RYVNuQ6{2 z_{>$Az!>^pYYu!?UXNII?9UeQ4N+hd5+deX@uEIyyEOz%8)`uXQNVVsd|b@J!2}u( zrU+%EJ?$P0A z58(|<1IP-9aZn%tN)^yk%`Sa?ANhCE)3i+{#s9h`MK1w~k&=EiOndGC=S^^Su2Fm}V z>MNk4?7FsR=f0MiA|5!2TkW&1bdk?`KBTcV6vOAK?n`uoJ|)9!tv=1;SM`eVw-7^%#9E$!1mRr? zl060Ii1n}ND?3DKhM}_dca{R~^V2Yu<(Ns@0lYfnuTR%5A8qN+oJW0pnwpxL^XB?m zfR~w%_Z4P!kVP_`HW?a76l8)1I&8n?M!)(P_SJpsH&7nmS34>Z3lu1j%s0C9!|!h` z8ga!?Uk|3Rr9ZVoac(=;=QM3i4_Dn(L{MTIe>vkO%4Z^4@h$~e|^ z83gz$t)Cd3+Gx-o8cv9gC*Vl9-9B7XF0xFJb3?GFdHkK2-|vXuvO+AE{C-3Touiekbb(;*9!@ zF@F?+wUw;K7(TC=xjGF!LGm(?yal8Dmv7A0-iqk6ltx42Tr&L`xl$Kj00)N>aZlK?(|RyF!4X6;@{Y^01Bni890;H# zq$6HX>-FXbpOs)#R(h?l@7CRaewA1Ioxk!Cd-B*g4d^{8>+bHJ`(f|yE`W%Xw8yeq zbEP1w<8Fh;wWXz3`NKb~1npDAnX%C=FA$Sv*sTo_cZen_CH$1a#5*XFuyVg)oAgM9 z&$SSJKm&!3AjQcUUC7x$LWi6@+&$^(YiG?{Qww$IA9S68aj zUwV;zB3BR~Ib`@8zYc37B z4VT^Z>{&301L8b_d5S!8!{l!^IOPVt#>4w_)35g;o&iNzmP(Kc;u3WUh9dR$+~+hw zrmjEXK;L~Ot!VZ1d0Qe!R4BI5UBc3`?3=LAU4p};+L$F2!h%0@$_Q{D1#@RuJ@_8u ziU~)TZ-o#e{na?)6$$LE)N%`g6>zZ|w89Fr0x<*Xv%257+^1*oQcf$nPrY*b~ePCUeDyh{;EKEe$BKRk2-5c=F&SFo4UygF$M2#D}i zJ77q6J)qki3aEliZU z4XZaOM0X;dmU6o%g%t|Lg;0lF04+8>5d}Bu6s*vJe9a-L1&$z@i;cO}&Fxe02<{Nx zK|xe0A+`^*U3yq2LQqENJ6P`aIvofKhDknLk3Qb6Cq6yyr}^00+IqUr4Z3Ch8&cC& zfga0EnOO$B#6!TS8jP_Pnd*0EY> zPpn{v^V<(XelMNvJt*|VtDdkuMr5QdUhgoWAOJ?A0{5ze$>>cs@x*&35oZ#pyP~;x zKHhsUA*cMutf0|@`0QHyoGZ$!t$KPL`#V(SeN*Ml(|D(JRN0339I>%k10N8nL^LBx zpV?Xd`N^g@su(H%QA^DiLmR8f-Ijze{`qR?AN{7ntJLFXBl-A?imr1>3)+#ox8#f- zGhHF+bDiK1oX4S0lG=2Z%ltd4)s`V`;hJ0_?^R|=oot-v{beRsKhS3^c*kfk77erS4D0RnRC3;hxQhotu`S9& z#ahPb>oxZGSse6M+0ry=PCLJC3@rOM(@J&D>49XaFea%cZA9?&KKV&CV7?zy^FV1h zTjIZ=DPCw^>&Tf=^7!q>jK^rIJava0-i$8m=EY0izOjX$VbHYqzmI|u(?#}8RC%(( z-@e5{3(1RdORuB2vsXrOBk-K@t07lAxJsr3dbGYB(JvG*pergOLB|nA5^f@M#cAO@ z9TgGlo~$2k=9xF~dodCpMbSjJ$g}~JHH_Vzdu>*yD)ETm!*Dphs(c{GBCNQx~NRz`SpnRNiaShOEPH5Bqtb*D4sjw7x)d)oSmUEv zHO7p@GWf7svBIJqU@{)!Lh<`BBWHReSic03@b_rA_h~kHS4bH1C zLe7Qz<4Vlel78%u|8{q(@AhvkK$h>8#l*VO*e-*MM;<;IHxI9segwJ(&6j?`VeZ}v zQpv5cm?w-4g7j_2M9KU0W6GhKWITge#^R={Jm;S?ll$~v5=M5G3gV5&WO+Fw|9A&w z<_|hH*mSriZW4P}AOm?~8!50#C+s;PkdsLK3|AYlGFz^FZJ=GW-^c0tFmoiJ{>9Uh zDo=6<4`_g~-IWVoz%ZZcfS>c+9VbA0freeuTId)3Wt=@5Zd$YK*@Vizrej~;uT1vqcw)=Pey|9N4I5=70s?R-{@70H zLo^*g5=g0QxsI%)sVT}9E0O+e2T1?M_J(gxCgSR(SkdTjj4lAd&+B`EZtV1O`wJTz z+kmK@*aY9Rq?BX?(7+&NX#AAEM_mIL^rx(BY+(#mM#yqYm7ARwS<(7bap*_blKJ+j zue&T_CMAWYfnHqv)YMqtM;|ayk+);{ENkciZ{hM~gZv)!3LJ&Qhr;-;t3X43Bzink zFH-k(r$>M`92jjRf*4q9ptCn~D_I|#YY2X&rYb6_$+Z`ObV@W7OiiC)$s>}*X6EdcL*JHOl) zhEB-J2Nd5wegHK0@MsUD>=Y#;{)aK7DUw>=j^peA$KLvlE8Cl&N>mRQ zKF89L#yV4D1>$RGuf%^&M5l*89S|mM`zAIl8rD~1CVgRwNgItI`?mKNrRvO(n(zY? z00M~1!x_QQECzdi9PZOTS}!%*w{a+%N?JOr-b}m{Qf`thLS&d_Inh@INS!Jm=SSKO zC<2P27WJYP7S5oT#lv5}?0672_1h`>vMUIC{@)V|DKKivv)8v)KR9RQ0l{d3JG#)w z2w;WQv}|(c)h62fG*JJCo~okC=NTbh%NhvXRwVqdN(@!Dv%NTIvkMx3<% z64_4!J*!bkxUjVX9QZy?lZKRbq6mK@Z)_*<;(M!Q&7*%cincrK2lCf~E+2%>Xx(>P z=T9A$IL<{(n0bSqSpC>LBuki&`!iXh1$2$MfHvSdI5-S68uCshCjgGBFmsrfOnxNW z%p6Nco@u2++pYRml@e|`8!}1#WPpA07_$cH^E7r_38J4jDgl*Ou4@N5#W*$!j=AR3 zQ`h)s$#)O4EvHvafBalla+i&4hxv>$`JLvN)r{?Tm}9wEwKYwoirjwfBG=2c-uP4+ z#7O*EOZ6wNj4`lD+Ij;Y$7=eJZ2^{ZKZaUxUok_BV4 zg!v5UoQDSL^|I%Z@YT_W(S9qazo*HjneVq2d9nSLt+>`(_?nA{UA^;X%3L{v{f3H! z+!O+NK5Qr}FViw6dztN8&FiJuw44Ax-Aay30c@9tm3q6qOv$A|cDvu=(+`9>59e#! zJ5-Af1IarZO@71YN65vSGj=gfFRHlwRV(A_Yg};ujt3ftY^z;awxKWcoQ6(C#3K0O~S*UjM@b=7aDaWjzZ=fT?$anM;X15#d`!T4TqWU4il@&=(@9B~+ z99FzSU5i=W#26*XuW;Pm6~wr|XFVI9qUlK3oDm}V3tHC_#5;?*s3=vvAKVE?ucY{1 z!Nq2iwr^f;6PxBw_{VJS_N=G4=*2>)Lz%ns`@;wiiE(IVcdbSvl-nPOJ@F(ru*B=p|9?ndtsCZIZuBSGRY*R{nFLD|I%K{);H*@z)sYm zC}k2c2?h6n$2Mn_smtYLqY#?#JLN%xgc@SOqXC=GngII2FvG&cYu1Jr%M>||Z#UJz zvQ?TfE$Y+@%we8>Kf3XZ8&Zw;6F5Rn#L7Ye=r<(0-Z^9SCu+ z6$xu_=n~-ibn{cpNgR5u4I+CJwY+udPu~i?yJ_7IG+4t;xFeAFv(8>2UmC??V|x2@ zW>l}JdG;3QUk@yM`g4|gYpC`-8k^vI;$r1uVxj3P{%dJD{8(O9M13|Uf@7h~Ka^v- zhZn&*y2QTM*PpVUp{CZ_JvPk04i85|K^Xh_LvWrD#NI6tV5vpEWc)G=)!RG2fdg!n zHDZU+AMxAph+OGP=P44QI2uy{8na&>r4nm=;M7ne0UJ71mD9D1SdrK)$*)VGt$4CO z`?w}*5qUXz{d2X)F;DM|gs8+;?I!uh zi$&8jNR~`i<9nn`CGtKky{K&ebjySY>bTissXn1xmNnx`t)5jN;e_NBdO#*kg&2U^ zt3V$G1^uB0@B6kY0(1~8^{u}pYn(L&SS`CsrPF`dUgl0k|I3l8&%?I=3u$l}DOc~( z^s7-JuAbWrD{|(-XBXv4{Ww*Kl7AIWOCK#bzTMngf#wO?pQ+Ijg^Enq&UP7tjps7> z&k>H+u$|@ZAsq`SrNvt1{Tokz4VgnlGImj6g1E#p z(vrQMGzmt6NE(9z|8|7_J2FseYX12B6J67^t{SL&OxnSRm=B{MN$FqbDD(V{m{Am< ze2e(^Hn4zPd{qyGZQG63BNW?F5*ZWBynysl9JUGH%x?wHdvw!AT~4p32x|u6bpF*E?^JZ5L-Fy_6EhQr^Sz)^ zQ8TPYzQO`9fy))8WjDx-+(^6&o;99quC-x~;3LV3EYhX^Dw56^3#bx83)2yceyse; z1Hv}a5iRHe$a+=#99u2`p-mHzPBo$~z}ns0>uvVjJ&FZd6<f{&y@Z}k6~x0CWO zrD7XTUrvvgE4JornJcLtZ^m4DAo?Q1mKnO{*P0J;{ic)U&?2D;K4Q-dnz>&}oS6GN z7}AV;bj)VA!O6y(#qIrWq6weG*87zpHagAnPNH~pxD2*Srl|7t*XOq5kKT_~jxSD0 zoaN8VhPz4!Z%v7t@ZRaMj+l9}jdbj3!wClP);KE#2wpqc;zvgnzej^@fK08~fah!d z?iDska_(IxSB>AXi7|_Z8C^oZR5=~xGvz!rk&`?IxzLTM z#p;xk7k0T*nf%WYlW)n!eaN=++TW+IkDjuoNl03-I(jKcu9jyfyl|SDN!O5KJox?i9RB|87V5;)RetzjgwGeJCaUg&@GE zw;cxZ01cx;4+9VV(*>L;I3OutN%(QXarR*VDi;?ElJygmBNua@+($heJiQJOLPL~B zO;0;Ak6Nw{MvRj(LQYJ>Wqsu}U9@(@hD(Zsc(rrc9tC}XBicLKi9z zE@Ovt)UmMeq5j}jpD(y=_fY)&7}BlNl99oeS9VaeP*T>aNGEZ$Ls`8syH({rw5+k1 zkmE}rV!*8`nI-tQ_$31S+<-46Cz30V3ybZ-k z#;D@`B6a%0j>n_8#<^Z$4j)-e{P!Mid<0hvca@UV~8Ry|Q;6PH@ine9u z#TF0@VwH92d5`_fChI(4CK#B%GGUA<*whiQvPBAZG&cg|%}r-cd$>pI{AiUG2lvkO zp+kD{ER`hv*=gtrS0+0t8$v8`u#rq28lK-5m)evGB#4Rf?5bj>TK)!%PN$Z%%vI z+}yjLw?zyy2DwoK8yxP1yk+SQ_G-p2w96;iz`qI z>pcs2L#UMH6{cZ$k95ERCQT2QSu^IbkDF>Nn@N?c?qtV!MrZi56ZP9Wm!)kPJ9^RK zj^+jz+4)Vil0b=Hi{VmArFLFiY)li;jCZTFVd-;pa6pf)`ai&lE8FYPMH}y^<^S}S zV+yubQ)0X9$@|(cJtCY1In4gRY(Ejv>dX2UbyI>_0!?OG&rvy+&v5|Dy7j@oFCp%S ziG@fX&K-ciw1^i*y{8bOvd$KUIf1}qd_9@pb4{voDJpn0>-p-ZRT%XEuAv;bBWq*Y zI<9QMH-_F8BM%Fq2%!dqCry`!sQGkc|3t$QcY%|w=qcXFAu8l2Sfr3al&Z8Yv%!&% ztBApgomxpJx?hLh!Z3y_pZ87)l0tb?L~@g<1ApI7dnH$%4~!jTNxpi5R1EeRA}_|0 zVa-K4vPC}05q94cn_F0*vI6&Oc-s6T00$s+-qLD53#q>Q5O#&kpCYPmM$2HQJX+fMEguBS_sy(M!Tbk-&mw*_ZHYKSU!B?N|mE)l?<@Aj$0BS(KL%`6K` z8YBRd7=$*p+j{*P1#Nksb8#(JSlOCH45duX4sXf)Uei1Z0=a|Vu1_{(<6TvIKnTkf z$*+qr7SL8WKwprK^mUyfS zPV9Vqc@3sD(S)50W*}asf?xx66O$;nqAzd}TwC_~Z*K7R_hkHcGdhm60qu=7FzH=^ z3-M_?%)HM|@;#s0x0Q~Lax){2N4_kN$l%8CT4tfA&&&*iafK6VaP0U{I7l5L^WI6G zP^(Dkh0%lLl%!|6}}OLS<;kEb2O>5fgbCB~dUWjTq4oIam)ykS$vB=edxNDXUvrN{t5EYE=!x zmZF>vkr6s7X+yy^0UBp8*{|w3Oob&q-@HF*JgZDoIeevvoeYSLw6Jop3346<453jJ znrK5Gf}Tesh2d=fzyo@MT9sBr_h??n$M~^<9P0M&Zm5mX0nG=5E92lerl-WuCN15Z zTt$%UumilvFmh*-miZTt+8y57>yL{C_QvYz5yz&Dpu#?0Ho?o*inkSB58lg2zn&A9 zb2mTQ`Z;FC@3WzaRY?_Nq28O2_@#iux!{?F?dQ6e^NsoC7eAI6;_DlHye_LIn2F@V zH`mzqrF{`!9*hF|%YRrZyLd7Erm56-z+o*g(B7xV$?Hs{!PY|@@Q@^&W+C4=mfixm z2-Q|B0kJ0}b!5RmIxWGMo?hhRFIU;yas{FL&#tn7#f3oZitPyqCXzbGSH@k@8GRSp z3aWx25S=(Y|00t1Nw$tFHmRy7480nn+eD&s^ZS4iyweT-gxA@R%;{r9&1I_|9)d{_!_KAZrjUA_zTx@7f#2ci%rI zbmZ7EY^&C&-}S5XF3ywH%LS0X%pQaVcHa-a;~UJ52&rQjbNHmF5*#0#JAb3CC^}2c zz>(3pbbc9UKd0+mQsRxtkb!__9`(T3E(JPGCp@JWKQQ)9KjUy=d(N|-!iL2Hc zprXobtpKEvSMW*z)dE4N;h8IzwfdgaGfWjoiZMNCYW2tBg~k-p3ita2z;XeLuY%^) zQAc*j*N&vsj6lx*?W1=%6xu*3gBx&neK}E|gMR?w?wU-)P9^gcn zGXLyz-6`(;GeeF56;99ADIG~{SHmLS^{y`t_pX-Fz`HsJfz0H#s4IrCx@h5h;o+?d zEi$aD+&vHQLsx(QB(S|MWkz{^r#;wZ%Nxq?L_|zza%(^!0-Wa~4vIA}MFI&e{0up0 z@S<{XQ2?M*9raD*I~q2KDPsuWlUdokLvP!qVBd12;`eE(HEca{N)NaJ6lQ2bU$9UG zNH=t!%nXXC^3MqSB^InHT02G7xyr19Q|F|v6QQA}0)WanJm=@{9nE;Y`&5M$lkONF zttvIy`y1_Xl_|6U``*+09guHG}}zamv& z>vi4;+VW+4Q;uhe3<_f3<2u(KL@i9Y6c7;bs(p+C9@kffCmQrrIPpGybVxu$K@bSo zrGf{_73%qGf_V&(F);YLJ?q6K!mxBO3g{!|6|UDYO-!n)`Qgp2d>eQ5lhaQXJT=v3jpxwTbnrXh|Y{u@1lJ&w=}wS-r@5E+(w z*}kYwCzN*}*9%ERjhGGf)BR9v`tvrjIP7RGE{-5tL76tJ<4J45rUcQ8s{oR}m@S9k z?-4k|Bz2sT$9hkI8M(l0Ozt;>`p#jgK1Tb%T*Io#2h7At!_*38ne)XpqU=jq^7QxS z!hDh7Ad2c6L{t$_faTKO9~xz$Mj4P`&_zM7{S!EljJY zHnV1(*_gDekpj9v{4^S#lj~2HRs%UQr1n6fM}+vkQp5{m@E6v_^YG&W(1B(2U(sV_ zaiAOe;xn%SF}rt3F&wg6Nf!+9-HK+;x2Lpry}8UPkQ?Ug7uwt81jk+EEi6x54Tk*r z1FfO5Bc|mh=rjhKfM|0!$fqZ!m7<41a2z`L*x1BGXC~f1o8M@Nzc_f>;l=wY2;!~j z7&ECpP32l8>BEUMSE6+0K%fS`-0G5o!lgb*ZXaXjzCf_QRN_Zf$2vfS;UQdKm*f;E z7in*AZ%fhQ!OF^57BrAoU5~a&<@A9(;eZ25=$)w9a%5RlFd`ckB1OgFOA^GVshx~k5v%voe-tm+uUABs0Dmrbi zGkN-jQHk~S6B4VtBSLJ7cg=l$5}=OH8REB0=o=b%O`4!sMoq*4KB*L$K?G1HUdVLS zlN}x^DGYQ}1U5wo-57Qrd8aAc#_N~@$M9n6nGR^j_gDYC3jB@sF79osjnivTA#lS4 zYJY{dZJq~Bpq{|-O z-Y#)=ex7}@@I4q_N_ND0M#lJWEdZ)3|H|P~Np;Op?-pw4-Dii(6t~EH+I=(+oCUiN zNvhxp+~LvKaP5Q4s3Qr`+0?PSAbkPnrx4184)lyez#5T)#RigrnV~=oW4#I)agtkK zz-8Nqw!JVs=8*z26{*LxIEq|H4_POTVhH()Q&s^_J2mWz+KY-vf$XDcrf7MyCcAo4 zA|GcsnzZtRl#f}JHp6qgRnjWjB!ooJ7{~LXmi+wh=7Em}4_M4;U0=4WigraJFpp@? zRNe~syI`zD9Ykzs7*0u5Np3gg!@FE16TiZs9h+ZVI@ptRgC)*?o|DA@qX0)oBa5*E zo1Z;N$i1jRgR3X2AmJC_xUR)uf^q$QV4Shs{4 zLOsOL+4>*cpL(sZ&}V9vuX@d!i3bn0rrH1q5Wr5~2iQ8+z3Hj%-?>@$D_E3Bq7G>5 zGcnxc-2`nC{kV<(i^~Z^!9j!R5r}bZAU`AY$L|AHLDKwJtL5>7t&c?x6!F_9b>N%O zAVG`DGWNS5!Jm6VII5FjPV*mMOqo~FkV%-lFh{rlIFQS%N9#)xX9DmP-K)G|LV=0_ zO`r!wtaiA^sYNGE`lkD3eGA;k2zp}<7+&#97uO0XLS7Z0 z72{9I0jZtKtYjO1qqouDK>u+0fD~wv><0wyfrCJr#Kf}Ue^6Vt%gbUE8QqlAXKOgq zi_s_fEyyxjj?8ieo*L+#Uk9L}gd!Ty3AL%ZENhMs9pNG&XzHEUUFvjYAi(=0uWubU zDiGieHVnin-82|dTGwB(eLvV3@+Oy}8$W3|B*c#^*8_mw>)N_IR@et&tiO(opLk%P z8_{chfrsto{1d{RZD7nMpr~4S1?o2#E^m~1t~kQjjS`~)xAGqy10LJye%C8t{ieL{ z?&gyjs9IgDwvMIUkDS;2xW} zhc`@ry@j0IbrjLo$7Wk*1TYP|ZAyWqJUW6jRR1#g8S))qIZ9e`G2)T$z_fwg?w{F+ zN4-$h(N9#^EEsiMsi@mwxh?|h4(zS)r}xEOXvW0I0{1W)5^)?)$28Elv~t8a2DPkO@K51 z5INT7>r5|?uVrQNO+)Ypx`;TG*x;a=AH|j+04l|qN-@~C)#K{VA#-Q5W7MoF% ze20Q~0i&e4toi)G`>lPmE?*oVp7vC~zy8T2|76lkcFJQGPG3$6&X6Bd@_!{dN_0en zb&`7!#COC&aLBH-YOtSAm+S3_T55>Uyo@?rd8!s!)2&L$t0O=4p62uXurw__#Rqnf zSt#XFf{*LHDNa;ZTdHTxJ1PNYWDC~_*868Q7EG@p_kNHY{pR5?+$ z=h4ViQA(;|?I;!!DhV~b`*ps(1ZPJLjk5QcLNG)$2j&W&qTmq!H3s8KVf5KGsg!{D z*^SqpvRm}IAZqf;HAo;oKjJE>+BD$fugw(=6$ISaKYNBi*1-u9C;kBkr!p>Z^6Yq74nza1B$t(>-n}n*?O6IX?cQ`iyi;hLu1l<0!0RYGB@&L~0fb7GGEv($@a1177*YmWKoV@F`JUwI zyj}%p`Hxr-w;lPTbHv@@;J3au7KpO$r%BCu`9K+etpxgGy=cNZ_YMdOcod;ZF&__| z?mR;NRhX<&Bz7B*N5pfHV77cNec{S&U z+-KG$9x^7n^y8;Etk1t8nhg}z8|{KUtlWPG3<=HhxS1ZyL2cq-=fD>P&LW6NQb<|p z5i%kkM$55|t&&2wVzIgLQWKwNoilC)Bm_|w;^Ft#l@4qFW3<#H_zd4yh1x7@`gw_S z5;!fx>UetA(N9G22K9G%;Ke$AL0k%=f&g{--3FVpG}z(b==b^B0i zpDD1>a_C*>ZMEiZ%qOu7BvfQqPA3x$g#C3ZYr_Qa_9T!0v`VwY1+c^iump>V2Ayp! zEI=c1uX3rjX)0mR;LW$M{N`(tVwm!-J-35smZMl1e$;`V+4#K5Zf%_y)F2isx zxqpuM@Slfym!;!j$`C^msJb)b7tU+-KsjObev*iWcu|>nt5BsB2vn49y)yeL1c1l_qWw7B}^Cpo56E zC@h1|oSfuNT9Qa@;{$$NwAHL4%VZ)jX5BM(YJU@ikDvWY7&g=iOl*8y<3tQADW4Zu zlc|j%9-|ZhoIxB&rkaluB3cerKlb$=?5tz#=fC{lW5BoC@8$`U0*VtcqmFmmCGB~8 z&CZu_%}4wtdAWzSwJ^sFJ5c?nABX@m2$Is$lA!-~4Ygh=~^+0TJS>hM!=V7 zknYg{GkQD<*Jz0~WPa&hg@6HL3M;@r5CtlJkmqaKd;>ha>Adp75#=BrW!Q*~>>$Ce z{XQCt$D{~r8u9jVo<{ZArzFzaLccT0O?)$`Az{4V$r?cRWMI8#vaZ_eSN1r3NB?F! zHy=ertYgG4C_%Fs${1SI;R;wUHzIc+SM{%SL7traB&2-vO;kUxqBZOrS?KNI^>7$x z$<7YTSc5O&h|y{3TaPuGls^aE0H2XP!PQbyLL)AIZ(kwl(Y^NT`7_O@AJ6RbI+|_! z3aJcwc+JWB$PwXXW%T)+Al?h^8vxnN(nM}f=HCWlolH6a7ONQB&|z|NcNX9bP-COE zv7mEcU=~3GiE}zsc<9kXniilNvtgd4MAWZ4c35)jgx6{Y>mUN7WMV)Ws4Lqx;&zb{ zIisCxB(U_bZA83OZ7Pq#n)$@J_`3bq2dWC0R{FfdWrX9p09qFg8xU^X>~0>sD`W5q z`qe1(>fMUBsEX9TyI-dUbn9~YlrmsLQu3SS+ldkC+bYk~3v^>sz@N<@UJ1}+k`&zi zZnV7lmKN`83NU)BE2mH~rY0zJ+ELtj(%HasBAW2LW;?9G<=Lg|~$-%tFXkT^lNBsE>(Ms8f zu5Un5UJ3=$GW;*%8|sCh-m^vA0LbAnD*p>|AHO8bHv5Wt9n3_2Q`xE|`{?>0-Z3)h z_R+Hi|MPs@jhT4Jgbit)_4RcWry&{_naKX@3TuS%=Ed#L^PyknoD@C1Z5#t}X#RAs z%Dx0$JPEA$#JY@qHp(e?zCiv?;AgHbUyGWyaynaMk4WfU%ImKCuFK3XFCI}gMd%BV zVCVi|2Sw*JNY~fnB@d;c#-n%Fn!FoVEbX|YfBBS0cZE|Y%mgiuf9L^&+W=`0>|0$% zG0ipL^7W<6o@e2Vg^H(tH%{vWQRU2CgX2@=RO#@wU~wr$3iJ zlDr)XNWeWQm%wfwMa}0OkknhZbcN#e+yd+>SB%xmYXdOgOh5Ney`hGkyLNay%8{_8 z--@DAb>-#fX3GvE{1FVWFE@xM*KrJK-M1`ptF-%?CD&3ujim3vR>j7B0jE8HR;yvu zgZ~4A3IxL6ZC1ev{%etdFG-gGPFOpbr|C0*pXzY-k@E}G{T|PK7;)dVVOrq6HGNJl zd!PN{xM{>p8J%MvBG;{+t?~uRi|D)%1HXm9x*GcTH&_@V18&5|PFIv*Qnk znY0#&wZ<>$kTFkb$bHut9h-WMYDLn*Hv)Z#H~$s8?n@vh*tz%);K=j{bl{PLjGG&3 zfnup~A)r+6^-2SUvjO~l$a<6~FR2>(88Qskp(eEL!fIbTpFu|flM{Wb7bYw*@S z0jKY>_p0|SmIpcDASnH?H_3$s;s(0n)JE=F-Mu*9fYzha3Dd1IRWWds-k`9 zj7iLVQG&KbpqibPqe*M9`8t|~lvJQ@F8=0f2|n9cLcRF=2~berfmOK=$izQu{Y_)Q z4mThVwRkMP08pH{S|A~@MDTEdo`>b$rt;$KOjb>sg&W9ht$w|WJyzZp+dDVNm*tSx zKGwJrY1k$<`kdtPm@NDb7n+DB;tb7I*+Q7FGiRJsy!Zy7#&^s>zn{hWN;g<^6tg-Z ztP=4P$`QCp`X*W**_=8OfKRL~^zHDY-`x=jZKaWi+o9MF|En);_i4#*ZEdvi) zyCbIG%U?i0iv-w%FYy4SCVk}7{uL7wv7BC~$mf}iu^5>OlJe@P3HlElU=gb z)?H0)SPV!Fi=O#|HDHe4R7FSCtX+M%16gm+Y-Sjy7yC0=lij-eBzHw2Ex{9K`PeFV@s7`;}sg0){B1){0?Mr|0qYb zM_@KXl9FmZI7yjdHdl)MJuFp#CD3PCnNjEn0-61Vv|OF#rp9HlB+OG5c>}5L6ufd(h1z70)E}yFqW6P z89^zO(@aH-j&{^ePX0$F#}sq~o95=WAf))U$2V;w0^m))`rqh>E);wLm}0iZePcnF zEj_dv`#S0?g#t75wL}SQ;GvGTj;^oVuLAZkeo$&f!V7=*bL7iy8@N>(&6}q$D>hre z0UF_%Q)e~xfxTT9aQF_FDg@C>WB|RBg0YWVlN|j<`0<$;UafkqRX%B|q%PT5_^u6z zVO(xv=2V*81+-ku)jHNy^Z--eMim3`)r~?dCMBF#vcpq&2+%qm^#1`v!nVA<9q#?@ zY_-Fbx$`0O?QeK`ZjG#&`Xq_6bnk=;HAM4e0pXlNr@Jrwtq=+&X$X72Y3&1LZLsN4 zC0G1L;G74g^*JyU@NTuhLb-@rujam$XUCqeEB1H^BuIdCP*W%#zOONnGYkAtU?0pQ zwmPVsSsoqrU>@w%HYR4~7m<>@Yhk^a*Wm4n!CAt@t&P{m{b_)nSpkj)odZBfI>CJe z|3`srzLK@ z0?mi|Z%H8!M=bOOO0m@cr8L}T?l12s8UwDsD9vWH1LY8c54%oShyDXT-YX^f*k0Qj zEZT|}{fpf9$Bb`vDc)PCHLKsp+w|~praK_+_z-*#x*S9*^&zkwggMP(9p*-)s^*oy z?`Ol;ijM(i^+-MEn@p8NSb5FKfgcwt3a+puw`b1)FqNZx)$VNYms_mE1P1U|F%1>? z=32jCeSb|Qe%Bk@Lf{D#+r;3Udh-5YyN$#F_q_MEUJ zRcg##Q5@93usH!2OMD=(jU)vxfCPU)Ibg+4N9?HiCWmvQwteQot)M*ogH&01PINW> zX3J6)j9p3NynZMPEr($e`o~w@@HDybILZ>#_>{+DH-7SC<5&M@6W&PyCKxbXZ{whI zA(WE$(z>Ice|+EHwmvd@n}toJMsn2Un0c(ZuC;?Qd&9 zQrK`2fD*iu;LCJTup{UQ5me8Kt42~QyI&g{82IoRZOf@b-V1FYle}wWSgyVOgV&|r zeK0W{@YBNni&_hmtheF=o371VdWa3a^eTMmz_8i)I$p^BMB_Ig-75eziVxj>3JN zn#r9>gRojjKT|jT>c&2C0pZzXuo`54SZ-ziRMf#lTW%Ovfr4d`2k?^hYT5cQNMzG*ws06rAOtea!-p3?QywJ7#5jmm$Maq zlvu>#B~VWKmbs~GmL4Q%Zryd4TkQx*vEb?6?}gTMeYuNs6+Jbm)=ha$Ld+jTTUZp- zd(>XSnpyQ=V_S79x)GdX`98Fmf=)M|wYh+`nLdo&5WnDi@c-x(bM@o10B+X&c;dS0 zH#JA~?_?Xa$WSA*N`QnCt6xQS3~PUZ-<<#CB?OKp;{E3c&7s zg|(R4f$?~v5J-mGF7KfzK4=%knFqlzAs_Jpl9SKz-p@Jcw8Z0EbNH_#Mm_wITRSAt zQQ}0JS1QEb)r|Z`;h1*Uc0XF&Qa4A<^2^#AWmqS}@gzWr+QoF)w(|mT+v3~CCv%s3 zL(%P_4*GGdmq+tL|H>@CrS@QD4|W7FlkZ_eFTnoX&M=*d00;GPBZjTk@UX~h(Ycmt z&r=*=C$S}!7*Y(izm2_VHMIw2@O-6!A~&E|N`fSWRpo_fW6s^!hD;Qx5&2*U+5+#s znrn+W4G%`1-%3stt4@3T+6ZB`G*KtemgV|`p$M*s-Deu9@45?TfZN#GqPc2j->YB1 zcawn{h@6bPhxlTuNWTl$1IZOT^U_-wN6QhwfBe0=oD(St)v;qu00Lym@gPRsAW7e!ZcWng`hM{XS{S7v1>iN% zVACt6J68Mxk5N`I(^(=L)r#4Z*~=>WERH6^^_2E>VxUznl!WI{Nx0JSf@xCO*7QtC z@3O-H%?3w)Io()!XRm(v=d+ieq5gTNog_I?EGPfNJ=?F7t>oTyuhN#iUZ8~<`9|NK zE(|_v{{mQSYHEdr+Y9G^GOsmx?+o(_i8$IlQ8A@cYyc9(@&3=25n2JKB`jBr8{shy zS%B?tfSRBLTFjqOj(W+==8*&kprk(GiF+SHz;JD-i?mJEeMwBB%PLNyKO@9=ZmQ#0 z-t7Ec1l8PxO7j0Wj)c1dk|^N6K*JR z89A*$+dx<}%hUha8fY4Hyimzr2EX`0LspA8O|Z`^~4U5sy) zm^<+QsJaTMsQTu8m+tObLPF_o770-V2|-FikXE{DmzD+vX(<5#C8cW-1PKZ0F6r)u z|3%;L|GnQm2RMg?J-c(~H}lLh&&-Jm*<=%O&n#$>jCsy!>os4yP%M}5mjUxwc+PdN?M1c~KNR^^R(7CHo^ zlqze__dE-3R3LWxfcT6S+s*ENh$4MFS?d`(11{DnOZ67_e8i}=kjj5Ey@Gkr4J-WA zbn+rVuwL$sllFme$#MeRqy3T-daqFTyk6e5ui^cZV=6lO>`l<;{)Xq<@9mp&?UouK zIeqJm^zc8ihQX0@ir_pB?-M{B>-{hBg8$UhC(mC@^ z;CTGM9$`D#fbBeU0kLW!inuT2pACv@B>TU~-9o=O%WH*T+J$Cky5&YXWFN+I;|mjU zRW9+Uk@6qcH=4oDssx`4xtV54bN4L=2d+oV$X?lk;W=gnC58(9N+~j66w*L2w-=kz z1m+g!GGpQ1(V#|6;@(_fU)rd80Y%zxbgCgR!}iV3m+XEi4u#8<-^o`f6Q3EK@@wNh zze|>jL1ZLpN0#;J@qTy_1HO{Q4rhAhLK#b_(Usvz)~FZVWGtOxc3A6w8;~F%PX^hT zA~)LG#B=+8CPe>0A5i9FyN%)i;!4Mqn?1ubW3*BRWFVJhR2#UI0SY0B2d)Fs2pWUj#q0DEqf!VF01G#>gy=m+Z*LIMcy; ze6#wwme}h7d&1G1HQv1$)JM^518x5zwn5;ld@!g662wCOn$w0>W`7;7VGRS|RY5vE z!=Ks?qFuOwj3s0|F*BNJ$@Lqqp=ZD!Qhww6K1W)A6t{s9&iI{ zyp4=D%B8r^^c2N*f-nz4!($V*f+x0%h4<}$&!{cdmE2AF^!&6bI4hvbecfx7lqmZS zrl|Z=p;;K(LHEsz?eYx?RTe+EvHNsL)%l76eTRjTU-q8VsV>StSqS_gK?_D^g^gpz z^10^}fmSGVAQAxaJ$M-%F(dGSXKMZXppXuK>Tfz}l?}8Ambiy|I94*>vfC8DkWfS4 z@TC|&^8R_@+O_VI8*uX+JP#S<>!)w;ZEgT)4PT?f9cCIomR<{%oQzy1DM8FrVCtU| zN5R1O$)r6Yg~6wp$fj~M+Ml1;G-bQbKu5}V+#5%k;!zq+j`d((54G7_%bwWpuVtiC4(qU z9C<9axhmSaGdUv(cP$P+iE(e^Se<1^KY_k3cpIUrRNrd5kK3Z^MiX|sFy(Ih70Tf1%pHcGc&I(ii0ghqA~iMtwZ1?47D^|UO*VEr)RRU zRy0dK#{_w>Y@J?j9a;SxzA#NFk=py`|H|$JaxbG zZa(Q>&M(Qp{oADjI0QZT{$Xjr9~uw7+Rp@m!>M3&bR4SN)1&ae4Vq3ZhQJ8ph2icf z!Bh%x2_OT3Y`PDf_Al-aY8Qe!l(^wX2AaN^9ULCJve}i8uTqR(E(1RhS$MS zgsk-z+Jo?-;?LsnEvV++v89B8$WFAPf#%fXH|^2?0=i(m4;D^xh4m4^j5Hl)k+;Yi zWUQ1F;-+~n*^5vd*bxjhhG!t-$yP!F!U}a&I3)r$xxvh|o?~%6ESY`KB%a)Q5lDCU zH?8lmnn*`}^X&WeDJhW^e0X>Z%le%;7>&Hcu zGyAv(c1QNEUfsTO9D@A#trx_HavT*d7~PbwYloY^6gHn(3Q|t{F$B$B%f&PPOD*t* z!<L!K7VdAq{Z&UfNkDu8r?)`)r}HCXIEH|`^(3M9x;V&gijs24&T{M@$4kl zS!-IW!teEDU4}57@A<5C%Il@d71k3n#U0GtJWt# zCucN=x~U?+Xt`jmNt&Rx%YB);E>T<0AcH*CncYDCVoY9ju=-H=c`d;K%8i=d=OsgFDT5CXJG14=Nm618+nG?}w8=Nvg2TwP2ZWxuLJ3e_bg4^asjopd< zPFvZNlCABW#$3!A#eiFzHu@bTIMYYX>L|_$l2$UHIB7H<{4lJqx7a_;o0DPE((v9k z)S>3CH6{w*rUM>4V~vvtH`TVR=}dX*SUIjyKDj7!U>pk5t$x%6fQoMx^?!$TWmY#F z_62^$+dv+l`;28Q;f5<9{j5mu?IyUcsSd?#C6QQ)$xO~lbNtstB3!!lLf%uT@y0~$ z`>eOn)eZVw@~0y{jwVb_uGdRb%!8AWDZ_&6&!#TU&^e(V11~u*)v{K5QClwxOf<-* zohrVpTH^9}&Gor*ZeOKIm$dhk6Mg?zLxz|*m##jTbkE~Y-sv(G2evUC$XYlnxGWyA zRnts{B@|*!327|aYI@do8v5*XUSAGQ(d^ZtKA3IVPMJNn4Jm=FW@HYR{-&;LF2uT9 zqnJm@gr;oE%Ufb_9b7{8?6b#!N!J-B(@-#Q`1wiKT5$FUGrpxuGuNx1_Cwq^-x6>C z7S&^nSn+>=pk}e_M(V0mX+|)kvS#

SoiQ#?~I{>-)evH${>&to)+m9%WJ>E_%x(cUL&cxj9a!?su~I%GD9~gU1lHBu1PU|AwZ&0t{Qj-Y)JqABg?6 zRl^e?2IUjP0W|m5y5 z4j<9hu;x`={>l|U;5nVdv`L(~N*Q>2Q6k5oZ9;n*#u%ht82NrG)RMQ+d$9rc`nc#U zcSS;vB4yj<-x)Sne#PGxvY#0M8wkt?->Cd%VS)UhCK zB>L|ht1%(;*_)2jZsFzTI1h;w_d0EXnYjnOcZ(5p~@ZgIIw z#(7iq1mNg7tVz)_l9({q2itj_HrSOSO1m!4`Rr#nLTnr1zO8Z!)- zFP11qCDRmfE%Y~8MGGVrU5S`8V54d_BHH$60Tr3sY#V3fU_~NvWTP`VlVhp2wQXT- zRrkLqGVwavEhILK#Nl-9=pRQL=fOm}v#-@{p*e^mQu>=aYHG{X{L8wtzzExQSVOWP z``CCur`~rÔ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⪼U?uXI{(n9uWq>^0@$} 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%0Ddt5Tl)P4rR$6 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

^<{Y(5$0XrauJ%wIT%&rBC4y%d1*HIpB~Dc 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

  • BwA9tN}ok+J`L;>?>sa|`Z|dXk1OgsB(~jSW=ZBd*NtY(RjN zSHE{)_~L86L&zi`IGX4Q^?5`_8s#|?Zd#(_69jIOxK-elae9w8$EZc%$&6%bF;!#s z5m(VKIlc>bMeFE1%33%nC4CH?k!n?bx^;y}rGYAiA$K1G__DzTjRn_4XDq&fPAZqH3)K1QEf*8G9qOPB*DJ^i`v z?h7^+4oF%k^A#ioTw=)8I6n}3Fs*#W8!+l3Y#cnYA94wwOD>iN=Wh6)pP?VO24daM zuxgy1C)!SmjW-zp=4IdaMd+O&R_GbVzOGmprz(!-T?UUl zSP3h9 zl-aK5miyuMXN6XFlX876<4pfxb-yZ~u zne-^#&hhP-I)%Sa;ubb_Zdz&XLW5Qxn50**9kV|d|LaGzhR*72x|LP7)1xw)P1lCa z9jE7x$|x6mko7S)F~#4N8Dx|c3&hItN~9=VN2t{f0@~Dva?gps zBR%;^A+|XbV;olXdOY1qYgs1H3YWjRA>PIAD1*==Pnx>b<2GAaRcu$*AkX=nUSBYj zj5u$oU!oKr&*c``_{McGWReYut_U`%|IbGGq9>PYxYs;TwN_3DFI zJEtUa?)(aKKx4qCkApvUjkH^_NcwcFmeCwMU>sHBi~3a@0&n22m%o-QZNBcOjSv>q zb$^u@Qmx`hOib*2JAZk7Ub^^pdb2CH=h)(mKtsVWDGwy4s#c;KNPVxXbMSe-S>nEb za>(}DVafJA$KkYJm8Qq+*zS%wL4$ysj}lCzlgM50#rN)vI}%g|~NICsK~9 za_W^2PXf6M&ZFS%9^79je-iQ?m#?0jwN7SI8l;efnLcp8TBliHFPoRA!xZMw4Q+N{1CH z+fj(soh6v|EQ>`Z|LiJ^nGuDOzhu(<)2!hTdh%YNv4$=JyV1MAgj~_sj*0pYNnA!J zZ%r{GUiBDPL;>Z!JBS552%QZ~okS&X)8Bz=ng{NkCvYhv*3@MP(&00-w|o*)oyAkd z=+MneMjM5Hx)$?#N)J3C{L?*PLz1+T!;ONhjNs*=f{+W{&hxi}nV7G4iMEp8P7?xx z;U(`L@{oKX^iC}1TVYi-cI$pbueK6>>oOe5mj~tFq^dzYZU5BxDNY6j+q^Z7xNjmF zEfLBTuQwtviJ1J65hlDg;jztOUA!0{;j5oU7_l&*DYPD%W$Nz3L;BpVGCkd16PG|y z{#3+zyoR5iU0<#xP1IUz=D9wycpVUW4>K8wW#i$&kZj)sYvSPGe;VBFe4WDrQe6$y zw_I6!Z653tjo%_6T=#`Sdt5zPcs%Vf&CRXzqfNR_3YukPNOSA)*pBqr7s!LoY^_K% zYUsN4xdj`vFQ!{@_1~FF9?o3(;}g9-q64Fm;Shq{ zxZ#bM&Ev04ud%)9P#7->*7-oaZ!6E=pi_j1jQ@<0i^8pPE;d;C@{N{rzw|T)$JkB& z8G&19R-0XpABxDZU7nYDI1od4kP8Um_=30mssx?W?GqDNlR4ZZP}w@VAnh;J-JG>k zIH~wvgG9ZL&)dRvwctjWTg;>S_2xKnHVB#g%NHx3>(9jsIel5_?Pn&>L!>65u}X(W zB!J6A&~t~Oa%O`}m&B|3bk3!b_i=mdIr47|jTBS9=(68C7&%YcVcgPiuS?&)sfDh( zC=Ws>1|1{fxf=iO3#Ae)G(|rHoqrB)G^!D9*9?XcThxZKND)-BC>e?%sR@+9LBHE#x4amz{L0Eg3y4u+YjI!jNGPgKLpTN}y+7h$Jf4xXDsI^(`yv#`eK0Vt&NtFR?B8f3pj^ z5TQq5l$Hk7$(tnar}2mDWk=~3=936w?`57XhOCZ82D3{)B<{V`^Rmh>YO`%xXk5(X z_D-Ij)*JLaw2{N3yzqUeEWYC0bbpdnGfU>O^R>0jym5r2k)bqb z{3d_%$uq5qQgjdxgKDS3-)Lw5{6#$q@$U1YsfpT%JOT}-{`wb4^kUlxo{ZIf1oH`U zA${Xcq0lea4zhwk6D!Q?N_Wb+%?r2`Od?`(d@dm5yf7GO`M662#!5-tneHNgz$p`j zK~2N9Y2nIi%GSK|dCTUUTY>1ZkYb@94H2*#RK+CvI7J99V-DVQ0*iS+2II><-03fQ zyUilVSn)l)hWLd@1H~A{vY2Br5Li{%@L2;*R0fwwPFb z{I8r*MkcI86?0rObrM~sD3FfdoSHr|@Mapj#Y@WN-q>au4A(GwVan(~A}&FG`n6YE zNpm+SXYYk61=jbLkMHCx=CaeyK)^N93Wa|1{G9SqL0_By%Dn0<3Dc##)=wnT7N)*D zTnp(ad8%jREW9ctKgK7ZDqO*McKt&StL^dRyRD+3mO1uaiHOYPYBIvwk{K2~Oi|M? z5||7H^iqkgtu15ztE(lx;x07jks8>zu4OG_+zRfo{v)c`=G!#&+Az3n)#orJ({A&Y z(a-K@BZoq-Y|^1ecICQ$Hcj#{8hF_~bMVAY$%wa^zIzXj_kl!`k)axR-(6qQ3e;>F zou3}Ar^jfLx9ytsQl=HM`^)se?J|UA@Hcm}(ClYtzJ+0B18Ru8%A9?$f1ilp>!Zo= z2$sR?XBZKF!UCH!o$j})nwxbt#rR3{i({AbRz@m?to`K!DtE)BDac?|pn{H!$vU~C zz|JBpSW|xdZbc}bE}z?0J!>U4C#Hp@Y$hHqf_3`bLQR=@Ba;;U*V?n$rq&oCvUH8htZznDM)Jua97{Uz zVb^1|t;aubigWE$fe(Ufz82p=vv=?^(_S>wPo|xf+E6nXfrsFxcpcgE9Ms)la_Vl( zu=%K|SXqAIL;`X345gFpo=}!pqCBous?^44A{C=_cwsRBARcE1kEFw_J`ZZ&_;ZqC z7)EPtD8hs0|FzjXQ<(*9F$(C617lU+Kvd5J>x{XIe}`Hjk-dQhROkoB86BTYenPaw z8BlE8SvZv#=XRYWqb8F#K8C4Vr;)R#jiT3;nFnRqbsu$IFR$WW5okS{v?S|ljT5O- zrlpoGHoKU0G%YiFQd^3|xtO}%-`4&i{KShsq`t?UPCWAn2PTy~e?2fN&P|fAEmTDe zV;*$mL1_q|i!4ntkOQIQ0@;jM$=~qY@Ra%~(YF?BsAKwcJm*tPQDB-AL z$r^{D@ev(RB%utv^N!f4P;z|XkOV#zdrZ?iuG`(dAxregp_BxR`p4HfsGVp8;jHo> zHb*bd?{>b=8nrJrHhA9@cW`K~)VA{4E-W;k4C;5{N~G-OU`;Co(i=ZQs>uZlW+y*!%-YB@Z4Qd!PvtPPYr1m&7xZ4L|dy0Sn zDTQp`U$!Du_O6X!57w{{sNF7F`3!4{&db z9sdX>Knwc#demPEQniWF7#P)998^=o?>QkqRq&6)k|p z!!;*uU;POKLYic`wK)BE#_?BdyW@7juR||xEEj3W?*79L-U;EsT}_oCl;uR8s5LcM zzN}F{Z)T+#!eHYxr*n0&ye|bzrLY>^^lxy<&J6}xB{KJ~=jGNIO*71i*iB}dB4@Y9 zJKxWtD^E_zf5_2$#bNZ;({p~+npRa5oC2PG&v$w^qy|lk=#df%nfwy6PHb6O1a6Jq zEdd8~8pPj@xd@kfdf+sxB_WVy$*tB}qni=Gz=Gu*jj~97XJT9T_TSEimy7=7#LhKT zGBCfO6LC))d2gE;k|g2cxVJ}K#nFwmmFxwC7un2bI-8Rbk(v|m=<3g|nqbIoai%%V zES<`INbcL+Toh zU9!Z&!9S_xn|&|u(&%MG#=(G7n8fb#tM=sR4nY_Hz~5q##B@mJbAeZ|k<1@jt)S1% zHF;OPpzXeo1sFWt2K0`5R5eP+7bPcrn|bCVpQ6N#wSyF@II1rIZ}g^M?8zdM!U-f*vAv-z8Q2HVP20vfosE^=^w0Gm9E>?&Men6n_Wt0ih&L1tZ=CJbSQT< z<`h))x@I1|dgyPaM=zr5jpR@&M_fCgTAeY6V@@`ep<%ZysujT6)EwMlF-{In0;56y zDKm@UFPV4hm+j{LjMQwDUIkNmZ8c8w=ybi$V3pLxdM9^aStV98oXe`+HTLuNcq;Qr zFMu&JxP7Ogd{#79ocgdF#ES`LUMG4F?L&@3VOStwA=rkUk|ejQco6;z(@odFuU46a zM)GNa1xdSK{jCCw6oCVd@KmxO0~Y6c@K7G6XTiYAtcHFdpSV-B2(4xGQmPu(-RCbxAYo-fn6xcq8-79LOe>65RBS1D5oUBvHn4YUOUF-dSN% zftyrhu&i>)n%_gC(>@~hA0QcAm0z4c))~+2syZ)U8~9%)R4WZyV$^s!s2ufo+Zh4v zXZOyaYxIT4YtZ*$`ol{dzQhMPw`ur>|FvSU``0s08{S!~w!y^R0Al2I)pb-VUmpQn zDw-cA*g)e?&c--9OQQjB=vsJ>VyY2rqSK;-KPb;J-V<%{G*C{AHa4jEhvoWL%`^tH zPL8Q;L=W{(c^uAX)+Bm~Te7~-$oGx1ycsYJ2;{)&fYIAR^LxqDvyuNJkH)-PQO>X`F7)s zdt9*3AKgy2==((+y`OPHZMm)jI-AOd{vN~TgS{5|mbth$i|hhc!~glRZfyXh>2ATk z$agI7gt4vMflM&zp=5cY2}#MpnI3_|l1BVM?Kn4zK%CwgSrgWYL`o|!+@&KLz#|cS zf-Q@0sg>#a$pe>_2ae$Bcl=HiPx!=9^ei=gU_&MCsv=Wev~Y>vpCgKw#zTb+!5WjY zA`E=RWH_LEbq8M<$gyExO`(121C4iY$SDPh1+FPI>bP!o7|^tuATmaP1d~UdHkN=FGnTMO&?QSF!zfpiPuqIDT(6-NPt!RM zEveC6dOIWQnt6W{xKayLYCuYQQ*q%bRHwsuE~53nu<>HL#h%Aia)OutO{t^2=Pkp3 z_~kN8rT2ZxNbsC+cfyhm+Lg1uZg&{mNeBjJ0rmPooTvT)%4$axW@44MJjW+_r>>y1 z|8~NO5I2tm>gMg248`q%l;INLa_v$3NawGYr8l38q+JiKe6IY(^j(Xo>y17|O!V)P z!y_o!fz*FQXVK%bxoloeR>M>Xjl(ZVE9oT zt%W|)o!@8jrs9gtk#4>0WoX?YoTzk7A0%80Uj`DQ2&~&?cSYh7U*%37dz3aoT^o4d zu6C5xR1M&snB|v*f#5TF`D*B_sr#E3T0|sXxT(%(<~d2RLB&l`VxMRKIUVU*Xx6B6 zl|nl}diX20h_u-i2=EAc@AB()e335HpafF}On(fr>9knS$J0r8?ESAmR4mNwUhdveHHz>V!qe1r_NA0q z7#SnXY(|BJREJ)Py zv&-B7F}gyV4ttjZ9lqlB$ylyhK`@-R+AKA}RvE||WQQpkLW&IShg|Oo>yvZ(_W;_5 zL_B>u5P5Zq><#B&@jG07uSW12O0)!Gt?w`Fc`p5kC;@-&^-OcTvqv?3alelx2hY`e zg%m-;2+(DJ`qVoFB#s_PSi`JyA0DRSc0C6}OKYEDdOChsXsJamTqjNaku<%60kzFj zI>+99*ipS%&ms_x15wyD$MF(E%$j;5qRU~Y4!XZU9gT=cAL2|aA!k_|xg?l2 zmS1t~aWW8yFW`}WrTb|OF<5n;xm2MJ`nb+;(NG!5}>vHHO>IN>3uos!^n;ndc)U$C47q zVwb_j;U&L!W{<8P8ms-HGIH}GGTf@v)>k4IGLaP#=MW&m9`x;Rn(xup==EzlnDVbW=w5Swa9xjN^pyH`e3s(ws_2UGH&1}(}n zSaRvKvzt4RS16_6E9Cux0PnF^ypI(0&yfmv9fQ>`if0~3-7gDHA{}u7_`mO%H-$kf zn(=Xd_CI)%1?Sp|TVtIb{?$wV%qRv_k%cer6)yp3Zs#)i!|ItknUl0*>d!`-zBy}G ze#>!+N;RrS=|6)jxFgMFg5nq|_ce-`;WI#!Wr1P@J`G&W|L(;C|FTV>RPC)s;C`~r zBu`XJ1Hh7bHVF~W>ID8iOH$-8p!UfGbTODiekQ}HqUGZZ$^bQ1biEeglS*;k4)aOE#h@M0Eck+Ss2!} z7}CKSy`~H;M&d=_@9dlywgfgw_lS>zyc@gvEw&s&fiCAnZW3IO%rcWH)(PwREiY#Q zTA8C0%?%ENoMW3qTHJKcSaGqA|M{!MN}Jnw{ga14?r}kI?#Q>klo0$p5gpVHLZFTNid{lCu1vPaBfcl5$tIfUoebbz(PZbx2V zW|{olg8D!*nkat2F=`GPlcP~IRL-i9z70mPS*^bZtxYoU$2vxFf zt449l(q-z;y_R#>6#}Jd!k3r+^tdMEwQQ4U=!)K>DgN1Pj$t?SPajn+=`U6NQB@ZP zIDI`;ye%m?eYw6-y<561_G?HWs(m*R@~PXulynqi2Dh?`{z%3O`+s8BXX7<#j~ zK_eiiL_*1ZgC7{Dr#Amd7L;lbr{93Y*^o@U70rSA7p#z1!uQQ3NjWt2BES8(Bk*7K zYE<^7e6wtr6A0fI-GU622{+12hql3c^ z)Xm1m9P@(f>_+}5s!@o@ls`A@UaeLB^4K3?Bl*i=l^C7dw`=qZ#i~J;(1B4wXjVp< z%LCGEU2k~NRe}#A*`g@qfFfdio!b4(XF8WUZ4Iu0JMfg}du@;>q%W z$WERF8~86RM=S#~h^;@#br~j9yQu%%rap_q>L2k9a!wQnf|3B~P5cnP&MWk^xHYsK z8j2#|o$-$xkGP8{|1l(EKF39{R;#B{#6u&m$DZ3Iz>}wQ>PXKdNTW42X4@~?MKhL} ziLd^m|MV8Ep%fA*V=5Ua=)enEL@pAow72D(W}nSPdBy(S-bZZBsGD2s~Dg4mB)v6UJd=tXdL{P zJ4|@lxd8hlF`j={#~gFbbvX&N4RBpL!EEFEv?R5Alp4N=g-v3rXn5ZzanO-NIJ>SC z%oXLpdzsG{cNOpf670|wldfP?JlEKD1N!*>WVC1s19|*j;+?4a@OkIrou|2qrx z^W48`-{``Xp~PDuiJP*0R}}nraDVoRg6{_M6=E27Ox=SL=yFBLHiiG7K}pAU1*@Nk z=iivPL0{HoXk@0M=__E<w#JADFII*zYx8}X*E1kBFeJL3F>X6=ZW>^GhIsc=nK7JuM8yo zjLx+hF0?8}~wOr9a3C+;e~q0V-4>NMDQ-KQD=$!_P-Ar9`}yHf;nVU^0Riz}ru{JPZR z|NE=K#59woumPq$4k@8B_I@r`AOwe#nSMW3JX3`F_foUQ?ZovpQbKXLw6mRteTz|WUDO}xBA+~s}kwwx#*(l5?%IciS_jq5$`+AWoOemU*P4lPrYFK z*M;Btb=KE(q{|soCN6JMP-EhF-Y-*6o2>V|V3D7lrMOx*{l5_mH*cU{sP1%JEA$&n z@b;(srT@i+D0l>B=Ym8r&$6oR8HvCxND6;AFvKNp;$en}5GMuiKA2!kC8m9(d0wft z#V1Kb$nbRKqf$`MXcY;U`+O%n+|h9+IQR8Qlzu*h%=0{;ElP#Vq=8H6$0}54QCK3# z6!?LR1@}T88u>6?!WJ5lj48+Kgyf})j4*|2$$;ud0N9XK5=w8=F$KCcN^4Qo+q!7q z?JfTt7(>67yw? ztoE=F=Zl;>UsmoH7@a`zP@F2nKuSD_s@S8SNd!Jg_}&z+++A9oozo1zN(Q1I4?S<= zp6j0BGD~gBiksUa`=a(gF1Gzfl2ys127W!eoO|BpUS-~jb|nIhwTvl)j_>Y!Uea8` z-hm*yzGBV(t-}@ZI<|Qic=sOF_j8yW0v52Om-x(}qd&KAjCb>~(}C5o*%*}uZ229h zJdL}$t8|=$6q?Q6>KYD>A4)ejDz*3^^)ua2{&On9Y}T0*?%mOK2u8kmN&R-VN&%_3 zAVmFdss?d5cGms}g78=JTdmAzp~Uu(yhCr)SNH-v6d*01)i5!Uz@WW>`7`uk3?C|z}u{{p3GJo(B3xuNVX*#uGmobZGg zs2VF_ErO&5+IV9@CMB+|fB{NFK8LQQE*@RXj9(+hgl2^+(J*>G_Io@W89myraALl& zE0L2P72+4f=iuG-{*pgR^q^Y{bKxKhMJ%4TDZO`pUSB;qz$qAZMMs&(C3236h)j*C z#-qP%MnxNcTi4T| z3{=cM2`DhBRRHIEPitsA9c!01uB6USO1fvQar-G@Be#m~ z!l5&8pyYofV5iuD?{iL?8ixe-7c^Hfz$@o6&TX!#;t{d&;tl`p6-R;S0z@R?;l71& zuCW==#b}>ZMew*-kb=huAhN6X+jmcdOoR`~zoJ@3Dr+4izPd{-}F z=Lv=#sltzA_lGlX=FT0h2?xuzAV?LQAM8{Lu79lsWBU`jI_?WU$dQ!WpakwJl9j}w zjptHWE$*0ls9JvV1f^53-iYqC%|l;qxBNbhp^+`g|L8y@Q}81wra0yH<3&s7K~hH4 zqlY`}hGJceGNYJ6pCQkroK+v7W|K*s*1mEbNE&3#Am;9CC8R*k$IsSbP&`4W>vE#0lmkJw(W82kXPH1gxoT*8ac3R z)fJKzBWfULG&Zt4jWDHjuXL9SfKE;?ldlK6fT_quO5^KARt4kL606h-_&l|O+h zM?iyT^^I$U90>G;wR<_K_n=n!ZS?y(-&i`y3NVbadn^ncw9B)5yKhvU&q1fOTbBTd z?h*)WVCvu!o4(+%jR$~LAOFeZyx2dbfXH z(L-B=)6L?h89E;XX9AQZx6AD}h1pgegpV+iG5E58YIU^@OmTBtXrkX_H4LNO2DYYp z4Cvv#5c;MfA3xR#PbFOMQy+PEcu`70<#llRvaZ< z##ED!hJuIgH{7wV*QP?h+#rvwzVhmPU{AiSWa$4&sHscN7(;}}f>G;KIGHaiSA&_naCHiR{&q&Z5Zk@A7nFBP!1RnXAln)K#} z0%Z{SXk$_mb7y|aJxaWQD-4~|pnECeIE1jsy)wz+^flu&5j1@KYEFeoUXFm~- zK3;0ra#Kli{oh-<-;p`*#+;ufO>sxZXf}{ukfO==C@T(1)hLPz5Ptj@!zTDh&c5Dk zM2^aZh89i4KX&s@q=b)6y)I1SZRi&>H-n6T#4nbg2Xg3jz-VB4P!LD0cdGsH8f=`< zy}aF9UR?}y`0g7szkc(WQ~AFlTm6YUm^pxkN9avF#JNX7Y!L^xGo-`#L>g~$!V6Q6nHMh~8R~~QUkMhzv?w)BszLegN zJx!FDNm>JtXU>G#TNuO~K2gJZ7y`v$vqR4Y)0!X-ZkI{ zh|SYYS#Zv5tV^rM;$sz(^5%!DMDngJp1qyhWuYo_rQ-l~a|pbbg=10qQnC7MVfzrMbI zqZ7x8^-Az-`#C)tE9KZ1zlUe^wuPb2T0RmuELk^zsbjPCeP|`{@ra|RS!b_F+mWlI zXjDJgn~pLO-v{WjYjoY#-E|!w(3gWhI^pLIHuv~b z#}q_}(|u}nwog*&mOD-{zo_NDX4di|G|Bbt5AFHn?Hua%D#S3T0~a#B)LHsyAeyOl zV4SO0K4clqA++Xnpxjs(EXw_gHl`>v>|h|fCNleI*iW0KUb}a^Z}Z}ouPNaZQ23^; zL6#}9Ejqu~&2%>W)S$hX8nsA9E3=rK|j z!ETU)!%;6MC(B0WKg3Knk7c8^XAUZtdV#ULYdfRKHcWWcxz_fSI@(11?*SkITAmm| z*u(+>s=FH|aI}t(==%zP;L|GS{T}?$&I7rf@gv@w3|E{^SOJlLJ~cQIxg3;qApUBF~V+`r2MjMWExvKFOb25!SczYaNTZiG~P@bP37B>#o%)+FeQxL zMiqA0K#|e>gP|Z53bf!GIAO0?#vBD{WXdG_|K$R>;TFM5PzuS6*N|Udo0a#sE)h4G zUL=yjyBXms8@kT<_u(}LJ&9;7#Ftiy<3>>lOk!ykr_~bW&*JsZw77FZ3!UNbPE;K9 z+mI|c-kVuK7yQ##!_YiLP3-x?us6J3=>ELiN}EUj7>(Fy2dgvp1O9tLjY|h`5L@3T zXt?L9m6rzcD~S5bHLRD5$!b_*!N!R(QH6Q!Q{~1_Cn1A+E5hG@^mrwSoo#LUcKZ5;Yku8 z^cPK6JKeNRUc8NY3j{o~0Fw<{FU!Y;ySfY}BwriWF?I4YGLL3fl5jS56f0|;0}tlq zYVgAdn7d_w(h%uTpKzxyp%J#$E!Uh*dnv+Ydvxcsz-NVK!&8{xU~THR&b>WM$b8^` z*8A^F&2~fI1^mS=uq*IO8vN;@TRy%j+BW%^Q$=NJ2^J2bY?y>KG!k@&6K%Jzw?AiuC@DcRs5Gt=_f}1H z;}1q~!hL#*eL9krzP|2wM>_bm8v+v(x~-4fLL);+=kq$Md@g{!Fv${oxedCKvTs#l z6@(G}ca+k~74qqkH`C54N87fpzpq@u#bme@Bn!mZgdb-99EFzD`pe8v`Cob1XE8Qram%-tM_@^#v#)1gY#rZzUJezh2r4< zb~Fo%^78!oK?^^ZCV;JG=2!A#6$YCt=7c|j`fwJ3;AH7hX`5pcavs!bN@1U=fS^5T zSRRRtfq;s~AETukp~Q-v2cK%;n|srZc}=4UIC6iJBw40L$;v%?_yrQ{{{eH%@aSx2 z8D1m!3NDZExRM;B zh-W!sCZqTA0-K)jvEZ!9811}UKnr9zZr-uEBF26m(V>a1mTf~#MKf{4n@_Xy+XhtP z4Y1tc$IVN=mpaI+Y-f-sWMIl&n|A7t)oI(vTbYK8x*UhQd7xR@7u&CM{7=Mx7)@j& z;1O%ABB(-Fa7R6)vkMZaQOkcm?)mG!FOh*rpfl%3PZ{Kh`=7AxNJKm{e#C{rspgNA z#@T<(Gki~tIgUK*sk7_Vtpu(HjxNmLaGUxazjwGTEuw*#$0tWGEdHtFqrI9^(Z*uU ztYIsU}lSH=1DbN$cY6usBynK=5 z9o`nB!Xq|-tJ4V|?5(AZJ&)vFq!A&}^^zlXwxT}#1hoT15O#s4E4$VcqSAzy5aZtu z&6f|bL4~S9E|t;u6g)P5^jySX zUTCxf&(F8=A?7bgE57 z+f5qi$#3hAN^cX?;-KZ)*8N(ekAFPz4kj0S$o*)~Skb~kCKo}#ak4erf0!tmRqDk@;uNo5s9EK=(rdSW1F zK^Mtj)mx;+N3blKQNX!uo$vy9G2Cv&Sye{dS6SrbcQrjuZD%8o__(?p7=8dfg&t?Z^7fzh^r z2m(Tq1OVsr!~_tBpU1@I>Y;2YGygotD1Esmy}4x9@|ksQPG>MtH?MaRe_BxE$df=okrtA)XI^d7P6YC z{cEM0AiNmIx)ojiqEPXYSqDNN-BlxN=JukEQ@4;1$8(+Y)*Ow2*zatjDD5s5?Rz)CIQqU9j~?P zb%JhKTA5L?10=sF195}ef#+1?oR}s^)*boKzdHQ zstT>1tEV>D`&ZCi$CKu9dWvPDDB4H-wsrT7b31T4G~5s)(j~zk=R_d)N3q>SbcpPU zzzIdd_dW&KRaAL>x9EqW*To*6xLcbZ#H$CK(cLtMO~@55ASS2q(1`|G8yb(^A4C4%SQ_$46! zSf(a2=sn#vqfT>&M|L_gKEO` zAfB<(%(TpBA54a))_iOlJ`qO%kgQSV5Q}{N(7AS0gvuEm`O|&xf8Rj6%ydkVpLz*o zRWe-FVPPT#!Z3V_`-cQ#Rh5N5v$x`j_G&Cf0$YUWZ`ov_dr9pssgAM+@2WSQ9lqG> z{xM|dvwd+u6xdurl*p~lACJiay~^7l9Xh414h;iUb5P|S&=pEP+ej0H_+IHqadl^n zH$Ow{v|ZiYmF{PqC}FKO#K?9Ok(-W#j5ZdPaaq6t=t=xBK6tlJ)umj$O)8el;=EIn z4&GM4p;|HP4;DoM$3Up3tJnUBVh|8R>#N!2ASUvB7d%@Z%zj$3>tZ%?1Oj%|9zy}< zksWctfR`rOefZ}R5YJ< z={^N_!KPSN$@kZTrhCt7<5tee_D#DG6OP%zu8vi-@|s-D)-U=NWmVbIBT9R18tKaw zg>}uINEBG+r%@`o96`m-il~KZ{YoHp_D-|L`OCrISMvzrl13uVZ*XOpX#QbFl=khP z9B~$~1p-_fP?lsO+|8H;e39CO5SR2X-uhrVYo6|~R7>)8=1@h0?v3z?r!N9|5pi`5 z7R=*h(1#m~Umvh{sMF;`5sk_+_C;A6v1nI6yd1J6;l%efo0qA9y`f@)NPta3k2$LS z61BEl$Xi=d2#AF79MbM`{vPIxrMg?3M#zG-f-Zu1)hI20^7w-4uU~SqA)uZ4{SVem z@R=P;!Qd@b?mb-4dfL-kCr-F7X7GnfIXZWgw zlF>t-BTxGDiYQoh)jzpE%bDPTfg=3sG03tVs9LYp(Vm!+fgQXLW@_^2-|8-%ld_wj~oC8f#I z4h9-pi|$I_Gg=5alB!HQxt|u(aZFrpmjrM5Z;L>1_`=Kn4*!H;MG__J9~0oI0E$#)Y{wi>@aayNqBhRc`98Zr@6J;Ehs%U)f{}tzS-_j${ zZWBVh)(%UXZhF@b;ce)fa62tPwlphTSr9BG3y0wI?EIQ*&}ugpn5j&d`|D(rtkC^( zFtP?dA%FpNmyP&;){cNsy0kO>_TQ`x5$b!Gya%U=xgC9&3|u7%XwLQwV`sd>703-s z&av`rSPJ?jeleOlFxr3|r6X36^yymIPpQKxA0i#C)?Lc62>B$`Os9OC8<9z3c@5L4 zTHe7%_`(I~yxHV_h4!;;^*8Nrc%NSIDApsrYCgSDeUr|k#yI9HTs33li?Q@5?joVXgS06U$bMfQ%r_j<< z8*&>w!j&JVb`ZKGy)1mz#*v*j)V$UyQH>e(zlv2gH3bsNYORWB5V=mi@_Q!Fn%k7w zs=6u9GLbu;$og9t{n2V$3$1Ndn(M+`90*Aa^@1bcAd(x;^AQFF0oOu&`gm9&3VtDB z<^#?A533vnLqjO|50Af3t3u$b5{l?XcU>-QrSB$o!^*0pw|rk2VIn-Uo|ZH&ywFi$ zptVrwnha9AM zGd_AT#?Y)foxNnJoA#V0EFY>vRS&u3viPLm7HDvvu5>3-7=Qk^AQdDgAFD1gT~t=E zCD~?@vb`o7zNwX@IoM3=yHmGd=$Jv=m z!I$h6FbpkENX2qT>E4Z%H^$jDsLmqBy(CWZNL+NFk+c}d(chcRJD38y9dQzLoF_x! zN)pF_k*6n;90@E!bIyM?x}jd_1!s}fn6aVubNuWtOv60x{pROX8s4FTIOB{lA|LZ? z^@LRd&66vCQZ8dJ-{W{3;^9BU#t1BNqd2cj+a<3mgj53Eg_cRxAG5%frd(3r-f>lJ zEDis&2(vUvNsU{jXM8)O9}1VM9||er5nmw}#bovVgQSH>HfGD+i=>wgqAo2$`++)j zkq*SW9FPJ^Pg?_{KZ;f16~8 zka)m$Ku$#(c9v-?qr6dlC*5Twsl5w5P-NpFoz-+Ix8wYB`N5XF&7Hn(N+AQ0Kd@Hr ze*lcR4fsG!y1k=&kEYL`uN9g&?=NU7yzZo$mZswWeW(R7wNDIqPvl*vUF&gMS(wPooidVffskD-7 zy&JR!`vSi+BVf1+NBG9{xr0|pwz#ZN>VPWo13O%iXsmmACANr2R7Hc-j~SAnWq<5g zg4L(XXSG@|g-p&AJFG7qW#dWwmx(=qhj;&-GyU3McK(*vtFvz{zJ#dyJs?>0HA|IH z%+5}NEP@;*9-gt|g}eVo%TP6ImKm{cCzsU{&W1p`kGKL#5o)*~J?%e{wMAV-J2hYa z7eNI^3FwERATvBL8q5oHtG^^ieW+&+l4Xud-&kda5EO} zjHoO})-bSuIlNluf_CUez?EoH8bRVnmD1^_G4ktu*Pi3Tfhq%99${!Tf)s6;Qo4!a zKVY0UqN@@^JAS}Ds{N|j{eb=h{>8zZ#WMBL7@OOY%KD#PCQJvp2fesiS{zErdvZKu zzLzTczd%U^=-D-7B!3^)ilyRs0J#R<(b%f8LbppqQ^&gHStZ4vTCbD1mWK1P$AzZK zN+p*XwY>?$FN*zP#f`-`5g8pqzcl*q`)?8I^?`$o zo>)BlnvhXVB{NemeAp+PH&Qm~9Q*oe- z`O27HK#L^LViVidS4{`E-k0w)WfLJEzJgjl?s{2Hi>Jzb4bZ&D8G~exzx_TrAh%|T z8B@q_NbLTl!;l9H;VKZufZ2C1KI`R1|BEy{H8T41phnn6B@EF-EMFDv9oQg|HK9KS z0Q$nGQSC#Jp{o+sB#zgp4N8aIoLj4dQOoP&GNDQo?W89#g(*O*J|D?Z2O1;RVJ6hj zX%GZ$bI*(Jx!-Wb)qgJNXl|s#jys;5S!Seq<~99Ob@VD6QH+bO_g7C_JIOZm{@HIj zX%~_W7t-17RYe9aMFSm5ZW(&4=A5Q+H(?C|9fR4wJRAB?mR1H!FD=1tPb0sKiSD2; zEU$MOr8mBefGbKDC=wYfL5*4(OehrU25AHo#zBE5lu% z2cAA6?61ngTo_bwHNnm1HZM_j+OK%^p^~aL%+AUwG1)Y|szr3vF)c7~gmB)SlwW4I z-pIyzdomFAa>UuA;IVwB{rNX3Uy0iB)tRv;U>~W}D|Q_OM0TKy;Jae`ScEo2R|s=H z{DX^B5I`5L346q?G-6~n%)~Mf(y;$mFQwp81X@HnQ^}QQ2%zfjwfpTI6*N!waN%%# z{yiWpx8Cc9o}HO_$~^-N*vxXH*I#egL_OYw%eTN=dRX!OXu#9LMuycFTFy6gE2y{E z)bpCA*Dh60GKMC&F~s_P=`RBT#cPWasiXQXgLDW~I3RL)eIW)4mIMiYM%9oYNXv^x zX8=}yuQ#k5yuD!yI)SQp`%qonyi2y-omDjYJyvMdVQWkT*CjcooK$Dc^i)42~I!g))`25R^R<$N9Qa1pM&roF8JD6oeJ>9`3 z?nR_*Vi+)#PhkYby0wH3T}pkozPsWjG2oOK@?^yg!Rax5ja;2k^%>sx%J=_fv#3>Q zFgiN{{kSNRC(TDbK8TctKoQ~uV{oFtkRw7?oBY6teBBf3VaY1mbW4&rL6Y_IAyMFQ zDA6<8{0#RVK68ziKK?1P`|KAAlg@^hg&~7#Cx1RP4)QcmmFK;xBF^KyDpYW5`$?Lo zR39-B*wNpg5ouZpw5RQ35k)6Vb$pBu$V<@_>y<7Fbkd6`v8;`(@z%jDBTsC1Q5E$f zO6y(jwyQa3D`dKfQ@9%XC~+O{#CjzQv@(~_EO$|I*o$2F{Wy}EN3QJ@BQxs$Ixva; zdVuiu5gd#iWTM;dE4+y{S=d%;tbkFS;~iJEMB&{O@7#`{eFNeZ@Z_1W~WHe z*eIetJZ7rmSwC(;3DNzhU`WP3J02Sg+5Na!0{s2n=OT(lgj`h7`0)#Ag5xSrkw{LH-kXH?gD2fvE(>>< zb*g|Q6G$q?=HKJonEMB8@%FXE(=fYQ;g?*nEbt1u=L<3DA57-@jwRolRBmky0;LDR z;2#xkW*w$?+rB0f^2X78KhE09NLAsA(mY-UwgV*GHyCS^2*;cbuyPx$_4yV04mrPf zMMDC!6)VjqW7S(@17dz@p)RX86l7^&+m{W#5`AN<>u9|N-o1cZ1U!)4eSWeL@`a~M z?Ht}m;&v_!!Jw z>`}*>O9Kv3*L?kPJR%oKw)e84|FMfv+h9?9ms)#wJj%{5P16Qd7{jsGI{Rw)!>Y>Z zjWO~4Q8tM&M-&~--?MXH5!BbC?Cw6!+Rp-X2$eXb7Ze+Z^nOlpR_K0ynuH!ms>$T2 zG<$wp?0lUy6PZ1-y42FlnNTUPcy7#L#_15aE?rud_bOKyD~r)c$tiGna3j6AApgO6 zB@45SfRzG)c(EZ}-h8B?4;r}tbL)d|5*S8VvfMqtl*nSF1s&8!)gNM#POM>#aPBi( zt&9?y+>u=rk)0SOOd!6MFRAA5HUToP20Gi(S}0DT|8O3uvuNH!o6K#G-b9pCr){i} z75x$Rg!^)m1Cf7MKe^}de?T+#yzwNM{%OMF__f|FScgGeG+0lUp8!2-`p1$k(r$gF zSzcLq+alQ4)$VSIw;Z}o^;F>Jl?tB&55h?HUv>$j-#1! zLfw?1o)<=cA&+Hi;4+69u0EgJ7VOo%?i{}k_=Zx*;gPS?>I|zwfKG#w^~1Fx=8e4g zM&CdkqsfcncC`mh#tSpviYqP?OQ=LQZbESv=uYXLAE?}(m+0i^PcQh?6A%bLr&j3M zo;E3fm=kTInQfGD&ksMQzDp-AC+qW22zwID(ZvaMi1j2U`7yg$zHkIWc_pcR7SLoA=sgN6c{_P z%zTOxlo`GrEFOC9Y)nTalE=6b6Yt~7)1cPwM+`cpyo~W#o9{L{{x>POF%yMm>i;ov zhpq8Mu3*bQX8!}Yv8GQ^j_o_35%_~uu!YaKgo#*x7%Nt{%`W{f7eJvi)AzF=33Xk` zogW&lQG~}N?G?^5VbL@1Xq{schsbPa`x8QsD^7j21Rb=kUmRQ(T8wgXQ1A;Ip^@*N zzR(I4IKQ>j5XZ{)qL|gi5{n~iY^=~mh?ToK6y;l;D&g?2G1A~M1C}0ntSGH*>}oUn zVq-4YEz_7+T=I4tY4|0~Ly|fPz2c1SsX9i{51=_b{!!8bcu5V*Jhg_Oe)nt32f(~5 zi`WaQY_AD3A0}oi%d9Yk2zorQ$*2vQH;fRD$%HnG!Bq9XE4%hu&clpE|x%BOi5>UO`}U_Sq(ElNw1y6@05 z+;7_M89imS7&WETY@Dav8UIH=wM-!>$BuqqnkRcA0;xZeFU#G7yjhu2RmnWWM1dsO z6QkEI*2xiVghQ^P{0D)VrNGM?*F*~?vAA02EMq?!N#kd0n4CscRY^Sk?G_{BOn^1` zJcm|GN%CdbGICAKP8%UGjXlhlrNbfpb0g{{0XgUUwk8fa6@wh9WxKLH1@OCUw6pyC zy-*uapbo3Eyo?sB!*zHnu%NTw20PEPa;JoaM|3jCbQpPj?GNwaE ztf-f-?`$(CR@)bd%s!gfI;KIIQ{8_l2#m~gndGa_XoWvjDmR7eu0XOZv2yeks}Fh6 z&xpv9zQRXdI<(qkvF#COJSEW0mR05K^RzsEP_03*fVM}wQq{tCg$!>~@!i6#K{L;O zTHK>5gl+po^`YgWaKh&Uf+)ZZ4juem-^N;{vbDj>-{!@B6^!=WJjM}#W-Jv+ zvha%u_iuvOfy3dVh^M5?M#=#@l&l`yQBSmQ*;uBng zk)y&T7f_7=$yfh#LbjvEpiA!!e^XUssm^2J99&ZUYN<-0)?fBO9Pdc$c! zjpZx+ur}nBp}7ns#>nHtWy-~3lv+jC9ao%3EJ|?QJj!c5;jsOdM{@sT8~&zG0a2`% zPU8*4XlmdcUx%r18^?@lZ?)TG>mgFw5V-PhTbi9dv*x0r0(Hs6kp3 zL_(n>lZ6!ftMF`=_lZP=q+O$CFRtg2yr%1(Bsc4niZ^}0O+JKaR?0B0ZS;LEIUoG2 zy=Qv`NxjeHYPq7n0rM|E^RQtBXhj%0qS1hfde>0*rq61R5AUti#Zi9Q(nD;D=kBNS z7I${u!3$QGJUb?DhjECVU>3chMppNq5~Zh?8@&`HMsw~)67FwOq<+(WfAApEy)5A~ zP?s|8_KX5`Ac2a_`)3V}gqb&W@1*y^)bD>HY4iS@+~TSOLToi6_+PO3evcn(c(s*k zL-S$J*AzH$w;qgNzf%oAxS$n7jtlWdts&$0eddq*)jWoCR> z-RMnhaMjJqlEaxixHjK4QRNg^*l`}&>1~WlOG@HR^L=7a0x?x@JI7TxMyvmkI?v+v ziS79g%e_6)bx54Xy7J@&f@@&!&(1f!-FSM#ARtkp@qgB0o6uR&{#um8A_JI{$9<)h zw;t|0S_M$^s24m$4xX$$F%x}@N2CG(Z%g2HYtXmt&_I@6uwSG6t12xq~&5U)xZm_pg!6CLLIcu=cwtnms9qnk~)EQ5vTgWF9%!Wle@tr zOIRlE42zu!=#K9CSTX%1-(o2{m#p?9E&$Ex>b8QPE@~A!5T~VE7o(MT&kxkd5c?Ua z6#}Xr6{-=bXGoN4=o^&hu_fNIb*ZUiG-b>JT%L})&l^bJqg;sK1&G9<`zy`>-W22>deg5{x^_E+Zh zQ4kBW9yBeDdAuFW@WIVX>b~!*R4WblKf6*Pv0FI?#du`vE_S@fwy%=ZIJ2!>dF1<| z{WfdY*B>R-Q5P^QM&WlG+YL!RjAfdoRYl2ccyzTlXJuSO!BX7rWhE4?1kNXK8=fV& zXETS(pcPbli=Uv+bxEamv2t<8lDOHGdR4!?dw4jeGK!h1-158#S%25t8nZpHk+{I6 zZy++~`*cLn z%-5J9y1jZPd9?x=Y8!FeNB3waeNh{sddC%T@$=IHV0EfccYOM|z&>>P?u)JeYS1D= znP3{e+s!uMXQg(?Gf3n0;4d(dQ{n?XY_Tnb0*d;GQk{i&IH;Qn1c0TRD4FTNNWfQJ z6M!wNGu^}$ly6*1XS!)##w>FFti$u~Pq0Dj<4k)?tJ(iU)mH$;)ofb_3-0dj?(Xgy z+ycSf9Rk7K-QC^Y2MG||-5DH$%Rk?}@80*nE~*Ytr>3UP>E6A1t+n@_Fufg=WO~21 zK}0)t7YnWe0>kEU zH$+!7MMnQ!dT^guV|b1&pYD4dye*oFv)~-NVH9|3vijbs0_`jdLNG_Xnz0PT5DO~; z#85R%(VR!{v}?j(GYZ@{#B=x9>1XmUtXz=0q?J(QcYV7i0pWY1p0X-s5jzk z$)Qo=l(o@{{IJsojLrwYDYjQ^zGvMrG?Gv-akiT$Lh*Z{;mt-8cM7{;yIgAhOb#@D zff9ugwa}xV2>=3po%bm6vIRzF+qTFS7i8r1@TaC2CH7evCNtekDa>&R&dN!@<99FT znUknjT3>RPWdb!iWZI%tCrmJ?VKLbk%E_wsxbV5$>Yd(om)ty5lVmm?HagSNOUF~E zvUKJ*r(UlD`W=k?a*BwFOkG`FWgqpJ_}|t8K0j|RyRLRO3+6cqkQ%6 zv0T`JiuuPuT%re)G*l+ZRKi*S4HdYNXCXli*y&?@PtAD!ieY)#D->SE?>6@^Zyf3ooCJ3J%V<(U^i|j96_C4#vdM>KLEn! zPh&owM%RU2Ayrz~oZLVYRG9xU4;We7C$>3My2nBr5|Mj=(R(CZ53^Ereju1hM8x*~ z(!tZY$BK}vtw}si$hgVG2-2nz6Tl+>5k@!tCz4-dLPxBeIhx6P{t~9(-Fm&ruqwQF zjzdes^D?Kfg~~{?``IfLSUJQt^858;5{@J^CD|Gg zni|M^ewegqQ}F6kHz(qPMJcio9S`*Qoal6=-lZ|Q?EThJB6vUpuihkW?3kNVwimT27IpK-(<{Bl@vFN)+pVP1^TyQH7D_C{~qhVrlslE1^yb zX;)JNs($>Pi2n@+xLiX?V2I09|MNBP%p(;y);M&Xo>z+g*t4U(vDdgbqS_%WSNf0L zI!Si0b?vl|<3RKADPr~Ns+{7!E7aeJ0s^g>I5vOA$?-PkyC>o8khz`Ycl;)$RdHI$ z$xMvxS0m!@Z)X&0aD|-TtK(%sq9S1yUIxL`en+Iru*rJSCQfOwn?a<*I}mz2`iPZ5 z)>N8MtY75{vUY!$k0_}KYNIg>C5YZadMp+jRDkxta{zhYLH2qfpapv7+c1!Pa?8SH z_vlXF=iTk=wm<#_gWr#SHT6uHqEynsT_sj*Jof^+MM~_>n2_e7yY33ud9l_8m7v~q zs*@>U!>+occzYqw>au6L zJufz}`oEq+qPv8bw$gf<=rK0@X_nz&F+AFb=MDumxnsCpIz_nnOG@^TcIVQQbp!M# z;>T_>=|lma=diLaFBAF(SW*;>j)`$O+M{H8cyM}|R~|ovHJ{sVg5Vx?ey?(Q`j<2n!*cd>=iK(y>5ZoL@>R#fDk( z`J09LW7}arSpdo36g}+o2lVE;E>1^cfsYM5hzj{9DEt7x{}WF1h|UJ#p1Erc0H(+2 zHeR1;W#MImuD14M1dr;_f;U5(1g}#%ogTG?W{PlMhLSlv%2L~k?0;oVu|15JJ%(7q zq1YINpu%llzidX2MDe<3!aMPw$i=b_X|J5i%5{(|OSWhai80Ik-+Ayqf2B4t3(V*x z!-F?0NWx?N?B{mC5Qu|O$*P8xnD>dE%lw#S6Bg#1&?{p*katl|_E{r4zs#N9E^j$H zd1e4b2WD?Vt-H$%gx6)tl{q4VxraK9OV{)EZNPL14due2CoF{}OyTG;}N)+ey%2;u#0ryLcD|UprNnOjCZn(!6tRy`rjylqf1efeL1d zyk`U(l@zkoZgy!&PEQZmV~?aJ=e<<7Z}cAbN!aCXPt5_Kc(rfyd2eV}n@QmR-Z2_A z(#h0e6dyXiM%^oaxAcBb_%8U1TDU)+SAr*2W#0e~I+91zO(2rOWIO3w03|5`1U*q*uuaW|%}W*;c0`w1{NxH2+95xpM?plQ;7o($Cf0VIJr% zs$*yZEk&uGmfnfE^t4<2Ngb!0?=4PYMnw3gb}s4lEzZ<5V2{Y(NL#ovia|eJMZf&` zJ@S*qXhV)^7c`J8`y6#Sjhq)n@uHx=wHGnKpqSHAteX@(h!sjZD`y&3gD;^( zDuRbrvQT}qw-&lw_!3ZI^-}B9IdGVNAe!T@HIFB8)6O<%}pAE%>jD7BnU-`dDSJ%XVJYEZSt3d{O zQQA9-xg{hhN)X`xjNXNE%nQB{IWQVpmP>K!Wo#&At5}1H+afLKQ+&p4=T*fERd?4> z3q=(rqyt`I&%2Fq-Jt|^T%^AgJk6xI;iO=X!Idm{ECH>yTfdQeKc%~0UhoNoof3Wh zZ>mV~)#kRgMh75XpU%{k8?8){-BJ8dt|jx)Mjf=dS_pypepBZxUZhyEf9|2wt+hyX zxy1qQ<#rH5AA>NZ?4VE$Ds5wfJ$kaXC1$5i@DsF~20Vq$oV@#{gD{K0XCI+CC|~k` zoss}xI$5wtp@tf{iUtC}mShq$fk>mZN@~AT{((GhQy;BcPvd3T!dxQrXMDM@%gA#Z z-@=YP5e$jS^j%L+tcL3~bANgW&b3;3?|_|m@=-DCgR z^g>#5vUr=4tJ9j))C6Z`)$hR;+r6%97yLJ`GA$#eCgbadY4Z}sWM&aI)pHhi zSUcC{hVyecBhNw-2~kGx6Ju9o(Q#v242cV7_tO>*Fq52PSv!yh4doh&B&Bk{!@>WT zV3)_;DDl>wM_uo9#~rp~wq6|OU5QZ2UV%8Y*WL|arPYmn{D-3{OArNUHRO(zqQ~O z!_^{vSEos)4h2-w{En(vKtMbEAuZWBiW-cN{~cMHGQvNZuYN|r-5cV|LD~+Av!a#n zM&Nf)b`VxdVeJ;wk=>t@>Ijb|ULV^6QjPwHWyok^#-k<+S6hnjwFbTWuBAW?S|gFa z`ptT%YJx^IIsQ|kT`7N)LEbO;l3eaX-5mG2=@k#jlY4{W(9d`@E1fZwF>LuE(rA?x zY$?V0&n_z^e$_sb|9!wXfiEUU61papf`u(oR^Og0-m|rT+Tr2sad`pXp6U-RFB9`y z%o%{U6V&_Nx3f>#lKHh|RkXj49%xFdkGMh#qK7I1-?|^XyxPZXsONkwj0AU?tqRJ2 z{$$1jMe)s^%x^V}=j&Zr+iwrw{O{J6afom`fYuleAetSo3a@kRcV`RtptKXxS`jCwlZ z?$+Ya42HMGA4FjpN|0EHD@-sob|I&`dD-OgxH0*v?w+@9aFL_rZU48rIAfQ`nnCmm zHt2ebbM5u))@t8SgTEee4UFdJvsbPdcO`ofSrOZi@4kghu?71EHk+AJk5^8h&HlYU z!R`*co($*cvEO-8-6hEC=6$wqUB0fOUJ*(42a%B7n&jXYCv*nZaY(YUMnHHVbPTB3 zD(pYwnNHoT&`NPx^WGApuKD9K*GLK$EHrQ~W`bg#MRk^j4o`e!uUtDj;m!hw?+(fz zW#_9)sX;#d}14f|N97JU4T%pC0cn$dZ+Q{D34-=4)&S| zZ7o7F?j$2Ky7lTeUtiwI-C7=g-6G^O9R{0?u@crfeGRFJLRYA zePu0uB@1bOVb8x^-M4d53(Lzj^3Emkbx3l7K=D=zr#ByuZA-O;dhkN3Y6%x~wZyIV zUxM4O_@T_%#!rK=&O?5XA7uNb<|JoaA^=g#O{h~uV~Hz+J?a3Qi~#ggr26vpMDX)3 zs-5#I%qr&Sq9(#FK&D{hPnWE+je848W%k0RJ4PKJ|1d(!W1pAkCNm^yT9I0` zs_~TlOJ{8jFRcF9VDO-o%s*pHD@E=LdF8lK-{0}NeQfyb&)3h;teKilf)cB884S!Q zQHsdM!g5QF05?h|({AWpejUT#z(ySJeIXg;aO=fW-BobwY#g-9W4Um&W8GoHVL`abf ze5@3mul7W7$Mz_ytM4~XFLR7@8+s+w3BFV05E2%AL1{E0Ag)C`%|2~9!C!9Z@p=4x zrly((v$D14oIgbMy}}IFACB}7x$0S`yZU@*LaJXJyXTAPi*kU%ciY%+WeX@RQ}s72 zu0%l+=;;YQ{L3nEIznOhQX;ES0oM8X=VX^Gekx)BW#QOJyPSwL-WW`F%jgn&A7z+8 zd+QT*7l`E>&rg5_Ej9ts=QPuqZW2!FG%GA7YfbUtM$7Ul)4NioRyR8` zyoik}Izf*AeV@38F5DsWDaqBzWFB{L_N2$?9ApznRIn^Wy*kq#S-)+2{PB4 zSH3Q*;J>Ug-?D^1=T%q`bueS0^Yq(1kJW<53#IaRwqpAt2V`UDe|_ba;xrW0Cuyjk z-4iHH0^&4KoK9a9bGgp?ALw)ne?Vlw=aDAL9$W%oxrD(ur(mXbYP$}YgXu`mqfju? z>~*+*nw_V8jUa^l3vy~@)q*cS@V-B|A;?>-liMw{P@#&&S4SmQSG8Zklh(@o0<|Ky;qFwWNsNFrPw5=&s+|PoaNcY^oSvN@ z=rIcbYVGs}h1>{XLgwTDHR@PQcn99KWX&oVVDEXXd6P^q|GX|;b$mqSRbN6NqC0$& zzCpx+$TjAMxkJejxF5U2UpkyKc>5t}Ez77YF10#H*c-=1QZJw~T!h-p3~4TdN3cg@3YIbaeDJNHKAIxu*>c4Qsf#jhuuH`gs%K zG)g_%;r$mbFTg)rJPhgX_i}DYE0=*=)Ny=#9iH5Pwy5)7>By5Gx?*4SPLd6(dn@0$ z^!Jf>p^CSM_1+X4>5GB3I?1O5sv#Crh zwK@0g-j4;y(yp|(WyJVo1@ik4@zzdeUHSFT_`gDfjSN%`N6-)@uU8nG5hS#RN+^tmkaPi|%jE9GNZsWdD!?4nAE zT9RLmb>j64ZzA8S71LIjzrh3zy9afq9glGKWHGcc{vz3lVn2R5B#v!xk;fK(I-=)T zSlm2Zt6^4?<7(!(!>;pBGkV{}AtwCHcC8F8BjQ(U`Tn&s=f8-lk_-|ir{B8RmP=Cee%j>FuVnqO!u`D`qFkeK z=c}(^ZqP_BCt;so*;5SLX7nRnDJF7Xq{v?O)M+Ux{V}PKwGp6P<5Hg&Z=BvRaUzJ$ zBbzE%*{*;>+gd*ue9EFb<>&d2u17Nv0zNdzJ2fTY$Kop`XeAr8cX}ST4%6yBgKRT; zV!dn82NZc8S;?DcT}D0tPN6F3QBE2^dOEp72bIjUP?z>uX{!tR z&%=bR2+z(8QEx(~?u96F%^7qy z-52}aRqVKekn@hI&hpjQleLY>U6|N#!r9X$+W_BW%nUjT{<*Rs*~FP`2p{m#ujkO$ z!7!^a&w(%`vix_o*gSCXPLKdV+AglyG&`&6|29vo&Vc3ha%vGiQYoB~p%$4~gOh#T z;W)Y96on)>1lAiAnrv*8Hn3~=l`w{nlXcL7skrh6oM+SK55HkfV<-?_X^U-j>%6gm z%M+y(9KMJkc>ULPs^h$LV3jr-PbbICytL|#fkqI)H*7gtr5wSTlP4bGr_o7{rR6#h zI>Z=JgH^PaD0-ys>Ol$WbY+Yq=IaMKbdQ(VEL+*w!PY2giMlC$x~Bp%rMDn`uI#PZuQM$ z8T6)>bBDj8c-Mdo)`hx@@am+GVPXaSSq_BzD_IWllZRM>N-&~!m8q#qn!1^KmiE?E zSaC%_Jh^WhZ+FiE;^My$5boJ|cx0IRJiYe!P2Y~%`lROFa=mY@3TIC?+q*f7K;B)= z`)JSX-Xo`@(#aj|pX>k{Ur(_c&C%Tj&NPD@KIpb|+pT<6{4^O_BK=wFgMt!aa$f(< z!N&q!{rkS(*Ttg|_DP`w!NhOL4 z8uoZmQt{m_=vph|zmq*9E*(q7V`r30T}e1ZwLRMKQqqcQ495Lp)(0!Ea+G6r`%iGm zDZ6IrJXRUIx&yn|4)o)XQw=A#nrX>3IaN7U)oahkH;RnGTLWN{YRr&nT6jP;4em=;gplHdbD&KMHShFw2a&Cs}Ql@{Q#llyer>S;5+nc-{%)Q{56>p zH3>3R4n;OXd>vXz6i84LASyQW`;D`s`_=huUKj7#MrnZ5^Ir!;>V)vSl_F`aWZ4gU zcE)r=;3JRI$f0u`z>%uo;6N1_BU*`0hzIC=SqeBp#Cz>RcyPxpPI9i??rB>_L2E?G zMZVIHj;Ga}xobsb|-O#?p>E>@g&5D6MwNOdmsw59w z_Wc?vjYCLmaWzdiIW0yE3HB@&7`TgCyLoQz_#;@XA-n+CUMtOHw9_CCeK}EDml^q& zIYira1652#Pkm_g0Wq7$CDxXS7_O{JM9Zc=IGsz>P#)K1vcu@O&VsPAglJJE%(_CF zbD==@Ocl|sJk?u6{Qv*=_nN=j9>HI}gBHe4{*~ z)KCa<|LG-onLSqOef=w2Ru(zIMzYB3oPN*2*Zb%-pe~>wdh$xiY-Jgh+FWofa1YD8 zWX5mBW6S>|g+A?R5!h$!?Cd;X>S|yCcp&V)pQE>3Z_SYqXgDBKmy=agMC9b;Y;J35 z^0@M!z7hsp`G4ZuZ*({0!+&01vk3cLl-XL^6*V>Sp$(?w{Jd)u5E8I(>Di`E((;^) z9!90XnW1G%6J*-FeZI~fGVM}`wdIx>ITW{fen0fN`R*lESwIAgP;;!^-to;7ZHaVj zf{}8;qjtb;F7~xHU7QVdaeDh1n60?<0P6Hu9)mNMVX-0E-Xq_2N>5jKYO+*1M^Vlz z6EE;(5Rw#!Y2{vVt+?oGPD=uBp2z3d;oVB?&}eHbzx~fcI4JLzmyRXBDEu~WM}ja< z203*-X7P4n^_|8I@MMi3c>zp>NaH$8t8$-KS~(Wh;UnbTlwQoXFYp zht;X4F3W0hBrr=FVfBpWuT@0*9Dkc<8rUDVLey+vR7QZ z8M{XQw=>bo3Jgyk;@qBlCrQ3nK6P^o3(b3-)|pavc8tmbHxw6ygm3f0@8lLUIR^Ec zU%$RQpV|oT27GRRAd@A$qR%7IX|7@0cM$OJI`*!QxN8|{=96ft>7UByr6U zw|f4_5~cfav@>D27tjE*H=3jwll~|#ZUrM7%JB;ucdgkz--9u^%Q(nGMLvC-k~i)5vxyS`%;_?R2$X( z3Q@9x_)9uR3e*?w50S7q32lx?a3VR;fjb1kTv=wigUrw!J3;5?9qhjDZ|a~f%@v$&7tX4cCbE1wFzriMcjJ;_Nm`p`dTC5) zA_FzMKLef&BHH@uy`1C5x0rr~CJ%EAL-Z9i{9*rAGczVP(}(>&(vz7+1C_VQf;%Ck zJ^21Z*0EOE!t|H4f&RCU$zEbeeO$)defD3`SakdhT7bypji1FM$uyawYg(z7pd3kU zg}gZv$dv+ER}e_Lcyd$pRfcp`iHM=P%StX0ue%&S`RY1Omjj zKOjmQ+;l|Rm~dJgg2zFR~+Vr}}pyV_fplr#jwYt_xK3yXL- z5XS=ZF|TzEe5r4q$2k=XMihSDTRVD9fDs!hX8BpZZcF}8=$PZ7rBZ7}$F0n_}~C)-kc)d2uoiW{{eV&w^%Nos)Zh z;-j1SyM4zAkS6cKLr>TJt1EZ8){05X`BhpvLb^!5-91MdR87WqUE%1KV`R?mr z@cHv{`wgv5LqCnJ3QSnV*Pk3zXD&)Rfgy-$cjUcgfjXL>s{th_w0HoSsIJUm@aTT( zuT|f!Y}!9`n0=%Q`eLQ~cT8uGaRnvk!wE`TAAhn&OO8=^=585zdEoDNRGN{P>>*`` zL*5*x@lI?t;@Uw!$1E7|L^m8;^ZliXb4?sg!8~eewYHfRa=5_eDvrx zGI-vK0NWn%86o-^WKV7AG1za&q0(D-Ei|Xe53NQH46+%*OZ)1&;f5sc#C%G$$TMGc zcdiuny33>bK*y>W;BK$Up_q;*&95%dSCad(V(Y8C`0G~qFD&MI z!%j8>ChODIr8!yWC_pd?gCGy|rVaaef`If$tyHYdXXpH&Tpa_#3;o{NDbv?S)CLBS z?V)9yV#GNk+zhRSp%G+)HA&Fs2r94vElUABO1y|a8b)zclybDTfC2}{@Ja5j1hwh* z>-E~GW3Olns+(UwO)5YZzmrU<0u69FQZ>25+h{$nvCV?c-+W^K9-0z7k}qiVvhaNf z*|wpcHuw#Qc0k?BcL~}NnWe*?yCjT@6rFBY4sC+VNM^k>EbvVAuK|B;*>hr+=&bpP zg@3vTN>2a9T7Cs0d5NgV%`UIYI~ZLB!CJXru8iky`q-l@GF#Yw07sm*h#A;aop1b* zF`rN%)tQMgA!#r*{J()-4ktc>t*=91uwz%i5EJ?K)#D;Rz!Ha+k~4y5KPjnLomM4<)%vrR@XGUuI>qm_#l{Iu8@4GX?hi}{O*v{N_n^%?9__cLU z2Xe7@f|QzUGh7@4MS5t>3rs}ouB5>zwl)#hSG`*j(nAx|2GI@KfChFDl?u%$yC(k7 zzeO!DV`}Lc?(5$>;!1(`{c5X+B8js<8R)yUYgDW3Xw@yT)4AibeRvTdRaBI#Z6an-5I%VX2F97AHk4%2Sh@=euXWQjaduEzw{gsS1$pM=zw(M3MFHw{b z`K=OVxDZu^JRFT`Tgoqg#jXPZci8AAX>D`+r750 zbOoMPTBmk)`h)CW5D^iZ+uVMKXHYWlynis1Ny9<*A_vA@ANLAK?2rddY{U3ozel#8 zy|Obe|9!G;AG08rEJdtV0JYU9`Xyn~%kN*IZ{lieYrqymU-Gn*J#-4(isa%?)p9=^ zxu0<}7btsLj+?|-q7~I2(?TvW?k3^cG>xaN?S`m{7~n?2_q}3G(jw;ey2%$c+5K_k zA(7oyZdSmsbA1x%JQxbOTf>U!-YC5T^ zh|$s!_Hh{R7Pn5yo)ud{epS=2tpZXI7HV#5HNpD|0=DD6xlL(x*Sir((H3l=S3|Ho z!hxtTM3Tj9j7!xa1}4r;H|SmwTxyjb8dz#2L5>x>j}oPA3tS5}!?C?Q&#&c@B*WSV z^~;1I!HU^?{ea-a_C|sIk17R?qd4J}vd>N{%LT*%Ec*lBoU;K+le92N;y6WJzs@p z)Tb~G;@y*2rCg9>7b~jgJnfM2T20x7gnIKiE#zI`{~{u?XUPpohV4V}f?9RH2+h@n z7mGcI_h+HW(Jz$^KGK<3tDq`Ms-S)=`^;tT^5xJVIo9AJ3`XJL=A2jAW~n2J-DlzA zirlZBO(UXNja1j#bi05cFU{|jYAl*BCZ%jp7|&%&I%<{@XzT?K__XYIq-sC*^Bfxr zJRVvkjj0mHldCx&jmEjo0Vwp|gv;`gT9%{w9*2*|B7!$W4y#@KO>`h;U0tgzkX@Ot zxJfgLt9QZNtGIyM5^zxMj_ROJQRB&-sTd&W>k*g`S)u}k)Ax*8j_s{g8PkpQKG-_n zAG@ZIQ81}+^~VmqbR^}xKaE}~!Vi7YWk@Zfv$~sWa*J|k$-GO1etCrj^zaM<1T!kp zl}nP(H#>52d%=9Ssk3HW&IT#Bo_5H$-tS$$N&{B@%z?`#D3x6M5-Y;^ZcD=iTHzer zLRqVGdiE-hx5^IhOH@a;fM!v@y8$S2mlP0zdO^yU(505jp*AQuhmiFi?|TV}t_*B_ ztL)Dv|M$}V(E>vnuZB`BK6-1tGYS+wJd688-8RrEeUwyH*D3v`azmUGi5immvU%6< zqFO6%yl$MPT0$mTAo6_cHGC5%d&cKA(`6T7_9`SE0xWa!nd@2GK>1gnW8_T%5+>BL|POGo8jZE%0{bi3F)PhF0_ z?<4i`2#p%~*T7De`}=$mhfmU}fzAc0(O@0*Fb9(mDWf!)PmO=QMf%mjHi!HL%!E(4 zStOz&S!MnTcEFVfiZg`;j+NRj8AX3G8JC7{LGpfLk4s0~&dz(l+uTKG!|h~p)ENw7 z-v%4e6_GCs(cLxO)|S;$#%f{Fa$)fV2CPmXb!R6P##9FcyyNHvxt3oHW(-MnL z2ya$5tgD~##CmmUpwJ};sFxKdE+<};z}`-1ldc4IdM6kB!6TE{{ zf>m%c5Zd{0>P;br83L&=efT&&E|$sP-m}bVk*W`PG4GWuecgw4RtYZ+ZltauMs9iu z`r28BwHq48%V9mO)fuIQZ3u3h+~J`CP>m^}{a}ZD7O2#a;C)z_By}W6 z42*O5#vpwhS%&VOkwh*IDrY?2G2T&vf>CEc`ZE4%YYiNZ9<3y6G^;(oi<%itAFI=^ z*=Qx5*os~04EH~6pN@;=V#$33(P;HYkjf5uSqEcBkkDMCRffq6e_!Lpo=J!A{LXLR zf+^MH3TJ6LS-XaD$5XBei3wF`78V5A?iS#Vx&aF_v{CY;<`SBv!3&9m@eLL=*_?_) z9Q0UvM(!!ilV-jF7f#|~)Nhm{lbcxxiL5bq+;+|*&j2elu2viYkXA~*UPNJvCXOxR z)%8Qes7TeQ2o(}!rAS0S-cZc)P{!hQ*&H<#f#7mo=@}=(O!M_Fknjq;T)+EnyaBkw z3^m-Gu>{fU(2k{AWgfnrSNdB&3*?^$lBk4z2gN2TD4YWKqHN4Q01dKV0S5h0kZMh- zH91&>Pp4sKdsN-7e}?}+b$<2X=ls~S!3y})%+MpB%!~8t-D?7VeO(hgLkD(&)h6#n z1ri7g@ypmx@M*yDtm#a8crcM>g3tASsce@S__@#g^bH0>cNY&Ml^EMl7QXJr><^tw zbaBburhy`e?s@ED{}O#*;O}RJA)Z8Ft*9B}sS6S^5`Mi_$WpDe*wNcfO=&Mxx6Piy zW6yF%06T|GRXJ!;#p)eixxRqt0|^qERC&ux3S8yizSN+5vwIyQ8ZS^HB+6weEG;cb zQWVf)Bey8JMr%W}ZJCtt{Mm6S*dD#DlGqE$Y05TKhL@ZxE zkd&e{Ib98f*N?#IfQEy^xi1MEDsV5UidU$MO*msZhsAXO-bbB+97z5GOKPc@^)|Jf z_1u>Y#`I9jVB4QIj^(!{z&}1J6}6Ub*s@oV4BjJ$<%29Bhg$x;7o3bX&yir#nsY}9 z&3J7wGFt&KoYGE(I~prj@ByEJDV+y|uRv7o)uba<<%6}MC%-@ zIO|6HN@O-ubkKnzh`jz7VTH$-8Zr@GqxbVIBZ5`MRD)px93QS$-augD(i7Z8ZVE*m zpscJ4^%MM5iSf_n-G&><{`Kf7LWm^W4%0~QG)|noQZ*1#A8w#jDyq-BG~)C270g>o zi}*m1_GwQl7bMc9t>iI{5_q$bKf#lK{nK(u({i#Hh_*2$RcMck zTqNgYbL=jt7~S|don#pbZ=~ZWEFD;$#`$%aww%Lt69FEQ&(le;WT^*7`GB3L!KD_s z^fB5|7qAvsT&5%O)R6swb!^1Ok%^;GxpH}#dtTNOA1VEhW4$E(%LWH#*}$vRgr5t) zt%h#-)f#s{1vMMIKSy^Xcgk#nG$=#>p(4_-h@_YL4uFs-KRkY23cXB-B{l_`61s@| zm!@uacS}ueYI`d=W)aVM?J%k>_cR0d}_6M(wY(#}18M z{|P!av(0*Z^`Vz7#Cqz2wB;TQ)kd%*#6E0`DC>*`4+S%jL!f0@Kr58Nw1kkO?G|x* z^UlQ|qE}Wk#SPLzDvi(u3O{3s)i*Itc*HK;2yf$mFRb@YI~os~k=AlPw~w1m*7X;Q z^Z`w1y3U-EpSVydQhlZ}>5EWibAv4K!ZLlq=u|Sun3FS-}abHT21Bf;gRL$kbD5}uh z-(w7W*8>TjXrjygHyB#41VTM3N5B8F{W2wrg>D&_({vyuNqY3Ua*eQM&1WrX=ovaV z$;Ij7Q%qzXzf{BuAgnpeNrK9p=L>6ux9}1Y&@(Z%b(#qi(_zg;Jh85ctYs~p zP=#Ycvdr#m5po_GD+V0)-jdzgi0q__RM6gNFwo=sj%&U>1PhX9D=iWn@;}u^nM-`J z(=m%VpfGX;axC&JNTj(<2qdc=5ai9gQvGv0Y<#FLISovOWVWv=GCaTsp)|&dlB^VuIfp=-O*VCYizw_K8rUrz8YQc@CFR1^eady8cGVd38ppnTyx6wC!!g^6n~LDEF+~XjmJ?#o8@$s zm-bSR$X|*$jksxE?_HqK9L_`=sNDYD=JETe2VO_6s-A0ek;qh8`i*I!GKdLE`Kp{$ z{rc&-Y#&2QRl3|4p1KbEshxbr1F5WPx=>Lr5OtK>fE&~c3|0BJj}aJp211B$Zl^2Z z7e?8r=}Z_cX?S+&ij3EyM%1FC^o-5RN@2SczQZ+UV`+n+BW=lSEjd;pq3N-clkQF! zQkjyu^rN#RH<}V09FS1Nyg3uC^zwE&F94cOy`Qd$((V$x?TnFTFdRClM#2JG2P?;t z-l3Zr#kOMo(W2Gm8)ae|pnB&3zfdYU{YxiLUyo#AZwe*(p6ODoUq{DkCw%v@o^NmO z1ZB0!<7uh2rn8y|ijeJwBO}5TKWG~%JwFoz_iKzilS*<_F$NY0Rds zz_GWX6yrz^az@mdQS=GtR%R<3+BPjpLX+iaZ)0aHdXhk(KD$B8>`o>v$gc)-wz-GO z`FN^}B(ABVPeyOb08^Qh)A(9XWO}I7c(34tA!i|wZ~O<^dMiC_ zW^$&%Zw0eXqww_F2J!?75LHDQugtMyi#8_r>F+b1k?0gXy7jJH9 zAulNwvUh4{95F7Hnf-e#F`bTBU2qgSukyR|ENbMY3oLgJ8=Ug{L9E;t~d9X=E z1gnawS;}mXv~fgLtsyDm_xdBjRc(KS+=;A|0;_^z{N>yvVjp{A1>StQmk??Mv^;(kX6QwZDsV!(A%f4E6PeoNx`UdTLqKyKM*@fvuJ*{ zeB!(y--R2Pn^TKzm3j$=WE)u~Q5V;!%$B68S1Pi-%xw)JVl$&Oha~BSe=v8raYCT> z7mrMI-;RijFV_yQE-1i3hZnAsJMR7?KTyXs0AlXCei+L2caP9+^#%byKRE%xwS-O^ z5co~>WC*p=JpgjEkx)1g36HeswXHNdk!SI(g#!v#Vkaf2hCr_ORsGz?eUv95F*uO^ zz!(!_yWSlEs*e1-;#zrrnYv^c8Ql8?y50Ak@BP-Q#oNg(3b@h|o#_9j=pW;6YY;ZC|2{k+tX+WfS1x6qo zjx&M&vS>wq;)QO7++HaZOK|ZtM>qp&ovdQlG9!o10)-el3@yD_dG-q^=wZmv6_%^O zp_Wf5e^XU}oWEjkAyR{xF~u#IyyRd0Do?=AAF*H4-KeKaetsHWL%X%+j3TCz_vg8( z3@@F*o1ib#sAb?fW|s|Q7#!^j=*3m9i-g$- zRzQ<7q2J{nW8&oha4N$g$7c}N`@5J3{yTqUg2ITLOUE$TZ7U?Z8r*|JD@}6Z+k941 z^bd0=RK*}O%{9?4YvALWd#D5KuciV$%%NuCyvJ}Rsrs$$>ZpO389{|qwS$Vm@shZc z@Sxd--EyQ)=u(8QB{M>)2E!Jz8f+1IFDpezU}XHtH%}-s931}t7<&t#I@YFZG$BB6 zcX#*TY~0=5-5r9vdvJm~!7aGELvVKj!QJgn&UfCwzV}Mitzv3dv8Q0>k?wwatzJz` zl{BAv)No@^1Q@!EI?z08ijsVC;Mk{xj?;vdrK<|LWThp&?gJmLSY6yMSyrFI%Fy#O zt3obAdU+9LzTTF=iy;htGp^s`y#4B1;7uJxH)y`Q8~{JlSyLIvsfFVLdGZ~}i?<2r zNV{%}>EHM2G`Bwa1sKN+(a%CqD79FJD>!wvXm-y8FPFXyaC-X-mx*(kyirBC?g8>6 zmsF5oi{U$(&`H=3i-TBr6kl&CN+$KU;$NTLUw#RqTw)o~gnrG_*y~?*#^d)$54e;Z z^@K~V_(Tf!o5hGMX#d6#8Ht${PPgycyRI<`zg*bP52nMF_U#r`!+3peAsD0NI7W{O z?aX@)1zgY}qvgV?2Gen@E!FkH$8Vy?xvx=U(=*tXSsGxK{QKdJzG`F#;w1)8r-;yi zj6JBtfuox%t{X*!=a>CTW#!T5LG?@FY zOQ`wRlBIPFZPm=?Td^EZc1&yWBDC`Fas1;x2S$=R#qmlrb89j*syymvn%Ss)dR*zBA8f;lWFB}$2kwpkn?%O^^rW4-z9I4YoiagKc!r45(({Gyi zW%0!HcAYel&v@BCmZFm8S!dDpvVB$c$jV3TOq};@4&#{ZzL}5iI~CNK8S5J5egDIL zJPs>-Y}UB*iAXk(Z<`^NBv>afuEV-O9Fl@FC$GH4;rC<_a<1HqI%e?W{LQ4P@z$W$ zuN)$F6KnS@D(+w_QP(j*X-Dt51Q!taMc4yKh6LB<^IYSd*~?zNuFJBnQzOsmyg� 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
    KfhHz`W+1Y_iD~twQ5nZm+o|rVvln>)PkwG4yd3 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 diff --git a/docs/markdown.tmpl b/docs/markdown.tmpl deleted file mode 100644 index 30f9ef2..0000000 --- a/docs/markdown.tmpl +++ /dev/null @@ -1,69 +0,0 @@ - - -## Contents -{{range .Messages}} - [{{.LongName}}](#{{.FullName}}) -{{end}} -{{range .Enums}} - [{{.LongName}}](#{{.FullName}}) -{{end}} - - -

    Top

    - -{{.Description}} - -{{range .Messages}} - - -### {{.LongName}} -{{.Description}} - -{{if .HasFields}} -```yaml -{{range .Fields -}} -{{.Name}}: {{noescape (yamlType .LongType .Label)}} -{{end}} -``` -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -{{range .Fields -}} - | {{.Name}} | {{linkForType .LongType .FullType}} | {{.Label}} | {{nobr .Description}}{{if .DefaultValue}} Default: {{.DefaultValue}}{{end}} | -{{end}} -{{end}} - -{{if .HasExtensions}} -| Extension | Type | Base | Number | Description | -| --------- | ---- | ---- | ------ | ----------- | -{{range .Extensions -}} - | {{.Name}} | {{.LongType}} | {{.ContainingLongType}} | {{.Number}} | {{nobr .Description}}{{if .DefaultValue}} Default: {{.DefaultValue}}{{end}} | -{{end}} -{{end}} - -{{end}} - -{{range .Enums}} - - -### {{.LongName}} -{{.Description}} - -| Name | Number | Description | -| ---- | ------ | ----------- | -{{range .Values -}} - | {{.Name}} | {{.Number}} | {{nobr .Description}} | -{{end}} - -{{end}} - -{{range .Services}} - - -### {{.Name}} -{{.Description}} - -| Method Name | Request Type | Response Type | Description | -| ----------- | ------------ | ------------- | ------------| -{{range .Methods -}} - | {{.Name}} | [{{.RequestLongType}}](#{{.RequestFullType}}) | [{{.ResponseLongType}}](#{{.RequestFullType}}) | {{nobr .Description}} | -{{end}} -{{end}} - diff --git a/docs/v1/config.md b/docs/v1/config.md deleted file mode 100644 index faa5fe5..0000000 --- a/docs/v1/config.md +++ /dev/null @@ -1,39 +0,0 @@ - - -## Contents - - [Config](#sqoop.api.v1.Config) - - - - -

    Top

    - - - - - - -### Config -Config is a top-level config object. It is used internally by Sqoop as a container for the entire set of config objects. - - -```yaml -schemas: [{Schema}] -resolver_maps: [{ResolverMap}] - -``` -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| schemas | [Schema](schema.md#sqoop.api.v1.Schema) | repeated | the set of all schemas defined by the user | -| resolver_maps | [ResolverMap](resolver_map.md#sqoop.api.v1.ResolverMap) | repeated | the set of all resolver maps defined by the user | - - - - - - - - - - - diff --git a/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/extensions.proto.sk.md b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/extensions.proto.sk.md new file mode 100644 index 0000000..34d0a2a --- /dev/null +++ b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/extensions.proto.sk.md @@ -0,0 +1,63 @@ + +--- +title: "extensions.proto" +weight: 5 +--- + + + + +### Package: `gloo.solo.io` +##### Types: + + +- [Extensions](#Extensions) +- [Extension](#Extension) + + + + +##### Source File: [github.com/solo-io/gloo/projects/gloo/api/v1/extensions.proto](https://github.com/solo-io/gloo/blob/master/projects/gloo/api/v1/extensions.proto) + + + + + +--- +### Extensions + + + +```yaml +"configs": map + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `configs` | `map` | | | + + + + +--- +### Extension + + + +```yaml +"config": .google.protobuf.Struct + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `config` | [.google.protobuf.Struct](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/struct) | | | + + + + + + + + diff --git a/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins.proto.sk.md b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins.proto.sk.md new file mode 100644 index 0000000..6bbcf8f --- /dev/null +++ b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins.proto.sk.md @@ -0,0 +1,156 @@ + +--- +title: "plugins.proto" +weight: 5 +--- + + + + +### Package: `gloo.solo.io` +##### Types: + + +- [ListenerPlugins](#ListenerPlugins) +- [VirtualHostPlugins](#VirtualHostPlugins) +- [RoutePlugins](#RoutePlugins) +- [DestinationSpec](#DestinationSpec) +- [UpstreamSpec](#UpstreamSpec) + + + + +##### Source File: [github.com/solo-io/gloo/projects/gloo/api/v1/plugins.proto](https://github.com/solo-io/gloo/blob/master/projects/gloo/api/v1/plugins.proto) + + + + + +--- +### ListenerPlugins + + +Plugin-specific configuration that lives on listeners +Each ListenerPlugin object contains configuration for a specific plugin +Note to developers: new Listener Plugins must be added to this struct +to be usable by Gloo. + +```yaml + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | + + + + +--- +### VirtualHostPlugins + + +Plugin-specific configuration that lives on virtual hosts +Each VirtualHostPlugin object contains configuration for a specific plugin +Note to developers: new Virtual Host Plugins must be added to this struct +to be usable by Gloo. + +```yaml +"extensions": .gloo.solo.io.Extensions + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `extensions` | [.gloo.solo.io.Extensions](../extensions.proto.sk#Extensions) | | | + + + + +--- +### RoutePlugins + + +Plugin-specific configuration that lives on routes +Each RoutePlugin object contains configuration for a specific plugin +Note to developers: new Route Plugins must be added to this struct +to be usable by Gloo. + +```yaml +"transformations": .transformation.plugins.gloo.solo.io.RouteTransformations +"faults": .fault.plugins.gloo.solo.io.RouteFaults +"prefix_rewrite": .transformation.plugins.gloo.solo.io.PrefixRewrite +"timeout": .google.protobuf.Duration +"retries": .retries.plugins.gloo.solo.io.RetryPolicy +"extensions": .gloo.solo.io.Extensions + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `transformations` | [.transformation.plugins.gloo.solo.io.RouteTransformations](../plugins/transformation/transformation.proto.sk#RouteTransformations) | | | +| `faults` | [.fault.plugins.gloo.solo.io.RouteFaults](../plugins/faultinjection/fault.proto.sk#RouteFaults) | | | +| `prefix_rewrite` | [.transformation.plugins.gloo.solo.io.PrefixRewrite](../plugins/transformation/prefix_rewrite.proto.sk#PrefixRewrite) | | | +| `timeout` | [.google.protobuf.Duration](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/duration) | | | +| `retries` | [.retries.plugins.gloo.solo.io.RetryPolicy](../plugins/retries/retries.proto.sk#RetryPolicy) | | | +| `extensions` | [.gloo.solo.io.Extensions](../extensions.proto.sk#Extensions) | | | + + + + +--- +### DestinationSpec + + +Configuration for Destinations that are tied to the UpstreamSpec or ServiceSpec on that destination + +```yaml +"aws": .aws.plugins.gloo.solo.io.DestinationSpec +"azure": .azure.plugins.gloo.solo.io.DestinationSpec +"rest": .rest.plugins.gloo.solo.io.DestinationSpec +"grpc": .grpc.plugins.gloo.solo.io.DestinationSpec + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `aws` | [.aws.plugins.gloo.solo.io.DestinationSpec](../plugins/aws/aws.proto.sk#DestinationSpec) | | | +| `azure` | [.azure.plugins.gloo.solo.io.DestinationSpec](../plugins/azure/azure.proto.sk#DestinationSpec) | | | +| `rest` | [.rest.plugins.gloo.solo.io.DestinationSpec](../plugins/rest/rest.proto.sk#DestinationSpec) | | | +| `grpc` | [.grpc.plugins.gloo.solo.io.DestinationSpec](../plugins/grpc/grpc.proto.sk#DestinationSpec) | | | + + + + +--- +### UpstreamSpec + + +Each upstream in Gloo has a type. Supported types include `static`, `kubernetes`, `aws`, `consul`, and more. +Each upstream type is handled by a corresponding Gloo plugin. + +```yaml +"ssl_config": .gloo.solo.io.UpstreamSslConfig +"kube": .kubernetes.plugins.gloo.solo.io.UpstreamSpec +"static": .static.plugins.gloo.solo.io.UpstreamSpec +"aws": .aws.plugins.gloo.solo.io.UpstreamSpec +"azure": .azure.plugins.gloo.solo.io.UpstreamSpec +"consul": .consul.plugins.gloo.solo.io.UpstreamSpec + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `ssl_config` | [.gloo.solo.io.UpstreamSslConfig](../ssl.proto.sk#UpstreamSslConfig) | | | +| `kube` | [.kubernetes.plugins.gloo.solo.io.UpstreamSpec](../plugins/kubernetes/kubernetes.proto.sk#UpstreamSpec) | | | +| `static` | [.static.plugins.gloo.solo.io.UpstreamSpec](../plugins/static/static.proto.sk#UpstreamSpec) | | | +| `aws` | [.aws.plugins.gloo.solo.io.UpstreamSpec](../plugins/aws/aws.proto.sk#UpstreamSpec) | | | +| `azure` | [.azure.plugins.gloo.solo.io.UpstreamSpec](../plugins/azure/azure.proto.sk#UpstreamSpec) | | | +| `consul` | [.consul.plugins.gloo.solo.io.UpstreamSpec](../plugins/consul/consul.proto.sk#UpstreamSpec) | | | + + + + + + + + diff --git a/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/aws/aws.proto.sk.md b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/aws/aws.proto.sk.md new file mode 100644 index 0000000..d8d078b --- /dev/null +++ b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/aws/aws.proto.sk.md @@ -0,0 +1,114 @@ + +--- +title: "aws.proto" +weight: 5 +--- + + + + +### Package: `aws.plugins.gloo.solo.io` +##### Types: + + +- [UpstreamSpec](#UpstreamSpec) +- [LambdaFunctionSpec](#LambdaFunctionSpec) +- [DestinationSpec](#DestinationSpec) +- [InvocationStyle](#InvocationStyle) + + + + +##### Source File: [github.com/solo-io/gloo/projects/gloo/api/v1/plugins/aws/aws.proto](https://github.com/solo-io/gloo/blob/master/projects/gloo/api/v1/plugins/aws/aws.proto) + + + + + +--- +### UpstreamSpec + + +Upstream Spec for AWS Lambda Upstreams +AWS Upstreams represent a collection of Lambda Functions for a particular AWS Account (IAM Role or User account) +in a particular region + +```yaml +"region": string +"secret_ref": .core.solo.io.ResourceRef +"lambda_functions": []aws.plugins.gloo.solo.io.LambdaFunctionSpec + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `region` | `string` | The AWS Region where the desired Lambda Functions exxist | | +| `secret_ref` | [.core.solo.io.ResourceRef](../../../../../../../../solo-kit/api/v1/ref.proto.sk#ResourceRef) | A [Gloo Secret Ref](https://gloo.solo.io/introduction/concepts/#Secrets) to an AWS Secret AWS Secrets can be created with `glooctl secret create aws ...` If the secret is created manually, it must conform to the following structure: ``` access_key: secret_key: ``` | | +| `lambda_functions` | [[]aws.plugins.gloo.solo.io.LambdaFunctionSpec](../aws.proto.sk#LambdaFunctionSpec) | The list of Lambda Functions contained within this region. This list will be automatically populated by Gloo if discovery is enabled for AWS Lambda Functions | | + + + + +--- +### LambdaFunctionSpec + + +Each Lambda Function Spec contains data necessary for Gloo to invoke Lambda functions: +- name of the function +- qualifier for the function + +```yaml +"logical_name": string +"lambda_function_name": string +"qualifier": string + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `logical_name` | `string` | the logical name gloo should associate with this function. if left empty, it will default to lambda_function_name+qualifier | | +| `lambda_function_name` | `string` | The Name of the Lambda Function as it appears in the AWS Lambda Portal | | +| `qualifier` | `string` | The Qualifier for the Lambda Function. Qualifiers act as a kind of version for Lambda Functions. See https://docs.aws.amazon.com/lambda/latest/dg/API_Invoke.html for more info. | | + + + + +--- +### DestinationSpec + + +Each Lambda Function Spec contains data necessary for Gloo to invoke Lambda functions + +```yaml +"logical_name": string +"invocation_style": .aws.plugins.gloo.solo.io.DestinationSpec.InvocationStyle +"response_transformation": bool + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `logical_name` | `string` | The Logical Name of the LambdaFunctionSpec to be invoked. | | +| `invocation_style` | [.aws.plugins.gloo.solo.io.DestinationSpec.InvocationStyle](../aws.proto.sk#InvocationStyle) | Can be either Sync or Async. | | +| `response_transformation` | `bool` | de-jsonify response bodies returned from aws lambda | | + + + + +--- +### InvocationStyle + + + +| Name | Description | +| ----- | ----------- | +| `SYNC` | | +| `ASYNC` | | + + + + + + + + diff --git a/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/azure/azure.proto.sk.md b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/azure/azure.proto.sk.md new file mode 100644 index 0000000..d0d19b3 --- /dev/null +++ b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/azure/azure.proto.sk.md @@ -0,0 +1,107 @@ + +--- +title: "azure.proto" +weight: 5 +--- + + + + +### Package: `azure.plugins.gloo.solo.io` +##### Types: + + +- [UpstreamSpec](#UpstreamSpec) +- [FunctionSpec](#FunctionSpec) +- [AuthLevel](#AuthLevel) +- [DestinationSpec](#DestinationSpec) + + + + +##### Source File: [github.com/solo-io/gloo/projects/gloo/api/v1/plugins/azure/azure.proto](https://github.com/solo-io/gloo/blob/master/projects/gloo/api/v1/plugins/azure/azure.proto) + + + + + +--- +### UpstreamSpec + + +Upstream Spec for Azure Functions Upstreams +Azure Upstreams represent a collection of Azure Functions for a particular Azure Account +within a particular Function App + +```yaml +"function_app_name": string +"secret_ref": .core.solo.io.ResourceRef +"functions": []azure.plugins.gloo.solo.io.UpstreamSpec.FunctionSpec + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `function_app_name` | `string` | The Name of the Azure Function App where the functions are grouped | | +| `secret_ref` | [.core.solo.io.ResourceRef](../../../../../../../../solo-kit/api/v1/ref.proto.sk#ResourceRef) | A [Gloo Secret Ref](https://gloo.solo.io/introduction/concepts/#Secrets) to an [Azure Publish Profile JSON file](https://azure.microsoft.com/en-us/downloads/publishing-profile-overview/). {{ hide_not_implemented "Azure Secrets can be created with `glooctl secret create azure ...`" }} Note that this secret is not required unless Function Discovery is enabled | | +| `functions` | [[]azure.plugins.gloo.solo.io.UpstreamSpec.FunctionSpec](../azure.proto.sk#FunctionSpec) | | | + + + + +--- +### FunctionSpec + + +Function Spec for Functions on Azure Functions Upstreams +The Function Spec contains data necessary for Gloo to invoke Azure functions + +```yaml +"function_name": string +"auth_level": .azure.plugins.gloo.solo.io.UpstreamSpec.FunctionSpec.AuthLevel + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `function_name` | `string` | The Name of the Azure Function as it appears in the Azure Functions Portal | | +| `auth_level` | [.azure.plugins.gloo.solo.io.UpstreamSpec.FunctionSpec.AuthLevel](../azure.proto.sk#AuthLevel) | Auth Level can bve either "anonymous" "function" or "admin" See https://vincentlauzon.com/2017/12/04/azure-functions-http-authorization-levels/ for more details | | + + + + +--- +### AuthLevel + + + +| Name | Description | +| ----- | ----------- | +| `Anonymous` | | +| `Function` | | +| `Admin` | | + + + + +--- +### DestinationSpec + + + +```yaml +"function_name": string + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `function_name` | `string` | The Function Name of the FunctionSpec to be invoked. | | + + + + + + + + diff --git a/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/consul/consul.proto.sk.md b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/consul/consul.proto.sk.md new file mode 100644 index 0000000..8bed0d7 --- /dev/null +++ b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/consul/consul.proto.sk.md @@ -0,0 +1,56 @@ + +--- +title: "consul.proto" +weight: 5 +--- + + + + +### Package: `consul.plugins.gloo.solo.io` +##### Types: + + +- [UpstreamSpec](#UpstreamSpec) + + + + +##### Source File: [github.com/solo-io/gloo/projects/gloo/api/v1/plugins/consul/consul.proto](https://github.com/solo-io/gloo/blob/master/projects/gloo/api/v1/plugins/consul/consul.proto) + + + + + +--- +### UpstreamSpec + + +Upstream Spec for Consul Upstreams +consul Upstreams represent a set of one or more addressable pods for a consul Service +the Gloo consul Upstream maps to a single service port. Because consul Services support multiple ports, +Gloo requires that a different upstream be created for each port +consul Upstreams are typically generated automatically by Gloo from the consul API + +```yaml +"service_name": string +"service_tags": []string +"service_spec": .plugins.gloo.solo.io.ServiceSpec +"connect_enabled": bool + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `service_name` | `string` | The name of the Consul Service | | +| `service_tags` | `[]string` | The list of service tags Gloo should search for on a service instance before deciding whether or not to include the instance as part of this upstream | | +| `service_spec` | [.plugins.gloo.solo.io.ServiceSpec](../../service_spec.proto.sk#ServiceSpec) | An optional Service Spec describing the service listening at this address | | +| `connect_enabled` | `bool` | is this consul service connect enabled. | | + + + + + + + + diff --git a/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/faultinjection/fault.proto.sk.md b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/faultinjection/fault.proto.sk.md new file mode 100644 index 0000000..750183e --- /dev/null +++ b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/faultinjection/fault.proto.sk.md @@ -0,0 +1,93 @@ + +--- +title: "fault.proto" +weight: 5 +--- + + + + +### Package: `fault.plugins.gloo.solo.io` +TODO: this was copied form the transformation filter. +TODO: instead of manually copying, we want to do it via script, similar to the java-control-plane +TODO: to solo-kit/api/envoy + + + +##### Types: + + +- [RouteAbort](#RouteAbort) +- [RouteDelay](#RouteDelay) +- [RouteFaults](#RouteFaults) + + + + +##### Source File: [github.com/solo-io/gloo/projects/gloo/api/v1/plugins/faultinjection/fault.proto](https://github.com/solo-io/gloo/blob/master/projects/gloo/api/v1/plugins/faultinjection/fault.proto) + + + + + +--- +### RouteAbort + + + +```yaml +"percentage": float +"http_status": int + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `percentage` | `float` | Percentage of requests that should be aborted, defaulting to 0. This should be a value between 0.0 and 100.0, with up to 6 significant digits. | | +| `http_status` | `int` | This should be a standard HTTP status, i.e. 503. Defaults to 0. | | + + + + +--- +### RouteDelay + + + +```yaml +"percentage": float +"fixed_delay": .google.protobuf.Duration + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `percentage` | `float` | Percentage of requests that should be delayed, defaulting to 0. This should be a value between 0.0 and 100.0, with up to 6 significant digits. | | +| `fixed_delay` | [.google.protobuf.Duration](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/duration) | Fixed delay, defaulting to 0. | | + + + + +--- +### RouteFaults + + + +```yaml +"abort": .fault.plugins.gloo.solo.io.RouteAbort +"delay": .fault.plugins.gloo.solo.io.RouteDelay + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `abort` | [.fault.plugins.gloo.solo.io.RouteAbort](../fault.proto.sk#RouteAbort) | | | +| `delay` | [.fault.plugins.gloo.solo.io.RouteDelay](../fault.proto.sk#RouteDelay) | | | + + + + + + + + diff --git a/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/grpc/grpc.proto.sk.md b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/grpc/grpc.proto.sk.md new file mode 100644 index 0000000..54c6b53 --- /dev/null +++ b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/grpc/grpc.proto.sk.md @@ -0,0 +1,99 @@ + +--- +title: "grpc.proto" +weight: 5 +--- + + + + +### Package: `grpc.plugins.gloo.solo.io` +##### Types: + + +- [ServiceSpec](#ServiceSpec) +- [GrpcService](#GrpcService) +- [DestinationSpec](#DestinationSpec) + + + + +##### Source File: [github.com/solo-io/gloo/projects/gloo/api/v1/plugins/grpc/grpc.proto](https://github.com/solo-io/gloo/blob/master/projects/gloo/api/v1/plugins/grpc/grpc.proto) + + + + + +--- +### ServiceSpec + + +Service spec describing GRPC upstreams. This will usually be filled +automatically via function discovery (if the upstream supports reflection). +If your upstream service is a GRPC service, use this service spec (an empty +spec is fine), to make sure that traffic to it is routed with http2. + +```yaml +"descriptors": bytes +"grpc_services": []grpc.plugins.gloo.solo.io.ServiceSpec.GrpcService + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `descriptors` | `bytes` | Descriptors that contain information of the services listed below. this is a serialized google.protobuf.FileDescriptorSet | | +| `grpc_services` | [[]grpc.plugins.gloo.solo.io.ServiceSpec.GrpcService](../grpc.proto.sk#GrpcService) | List of services used by this upstream. For a grpc upstream where you don't need to use Gloo's function routing, this can be an empty list. These services must be present in the descriptors. | | + + + + +--- +### GrpcService + + +Describes a grpc service + +```yaml +"package_name": string +"service_name": string +"function_names": []string + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `package_name` | `string` | The package of this service. | | +| `service_name` | `string` | The service name of this service. | | +| `function_names` | `[]string` | The functions available in this service. | | + + + + +--- +### DestinationSpec + + +This is only for upstream with Grpc service spec. + +```yaml +"package": string +"service": string +"function": string +"parameters": .transformation.plugins.gloo.solo.io.Parameters + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `package` | `string` | The proto package of the function. | | +| `service` | `string` | The name of the service of the function. | | +| `function` | `string` | The name of the function. | | +| `parameters` | [.transformation.plugins.gloo.solo.io.Parameters](../../transformation/parameters.proto.sk#Parameters) | Parameters describe how to extract the function parameters from the request. | | + + + + + + + + diff --git a/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/kubernetes/kubernetes.proto.sk.md b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/kubernetes/kubernetes.proto.sk.md new file mode 100644 index 0000000..3267bfb --- /dev/null +++ b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/kubernetes/kubernetes.proto.sk.md @@ -0,0 +1,58 @@ + +--- +title: "kubernetes.proto" +weight: 5 +--- + + + + +### Package: `kubernetes.plugins.gloo.solo.io` +##### Types: + + +- [UpstreamSpec](#UpstreamSpec) + + + + +##### Source File: [github.com/solo-io/gloo/projects/gloo/api/v1/plugins/kubernetes/kubernetes.proto](https://github.com/solo-io/gloo/blob/master/projects/gloo/api/v1/plugins/kubernetes/kubernetes.proto) + + + + + +--- +### UpstreamSpec + + +Upstream Spec for Kubernetes Upstreams +Kubernetes Upstreams represent a set of one or more addressable pods for a Kubernetes Service +the Gloo Kubernetes Upstream maps to a single service port. Because Kubernetes Services support multiple ports, +Gloo requires that a different upstream be created for each port +Kubernetes Upstreams are typically generated automatically by Gloo from the Kubernetes API + +```yaml +"service_name": string +"service_namespace": string +"service_port": int +"selector": map +"service_spec": .plugins.gloo.solo.io.ServiceSpec + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `service_name` | `string` | The name of the Kubernetes Service | | +| `service_namespace` | `string` | The namespace where the Service lives | | +| `service_port` | `int` | The access port port of the kubernetes service is listening. This port is used by Gloo to look up the corresponding port on the pod for routing. | | +| `selector` | `map` | Allows finer-grained filtering of pods for the Upstream. Gloo will select pods based on their labels if any are provided here. (see [Kubernetes labels and selectors](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/) | | +| `service_spec` | [.plugins.gloo.solo.io.ServiceSpec](../../service_spec.proto.sk#ServiceSpec) | An optional Service Spec describing the service listening at this address | | + + + + + + + + diff --git a/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/rest/rest.proto.sk.md b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/rest/rest.proto.sk.md new file mode 100644 index 0000000..adc2aa5 --- /dev/null +++ b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/rest/rest.proto.sk.md @@ -0,0 +1,90 @@ + +--- +title: "rest.proto" +weight: 5 +--- + + + + +### Package: `rest.plugins.gloo.solo.io` +##### Types: + + +- [ServiceSpec](#ServiceSpec) +- [SwaggerInfo](#SwaggerInfo) +- [DestinationSpec](#DestinationSpec) + + + + +##### Source File: [github.com/solo-io/gloo/projects/gloo/api/v1/plugins/rest/rest.proto](https://github.com/solo-io/gloo/blob/master/projects/gloo/api/v1/plugins/rest/rest.proto) + + + + + +--- +### ServiceSpec + + + +```yaml +"transformations": map +"swagger_info": .rest.plugins.gloo.solo.io.ServiceSpec.SwaggerInfo + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `transformations` | `map` | | | +| `swagger_info` | [.rest.plugins.gloo.solo.io.ServiceSpec.SwaggerInfo](../rest.proto.sk#SwaggerInfo) | | | + + + + +--- +### SwaggerInfo + + + +```yaml +"url": string +"inline": string + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `url` | `string` | | | +| `inline` | `string` | | | + + + + +--- +### DestinationSpec + + +This is only for upstream with REST service spec + +```yaml +"function_name": string +"parameters": .transformation.plugins.gloo.solo.io.Parameters +"response_transformation": .transformation.plugins.gloo.solo.io.TransformationTemplate + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `function_name` | `string` | | | +| `parameters` | [.transformation.plugins.gloo.solo.io.Parameters](../../transformation/parameters.proto.sk#Parameters) | | | +| `response_transformation` | [.transformation.plugins.gloo.solo.io.TransformationTemplate](../../transformation/transformation.proto.sk#TransformationTemplate) | | | + + + + + + + + diff --git a/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/retries/retries.proto.sk.md b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/retries/retries.proto.sk.md new file mode 100644 index 0000000..11067c0 --- /dev/null +++ b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/retries/retries.proto.sk.md @@ -0,0 +1,56 @@ + +--- +title: "retries.proto" +weight: 5 +--- + + + + +### Package: `retries.plugins.gloo.solo.io` +TODO: this was copied form the transformation filter. +TODO: instead of manually copying, we want to do it via script, similar to the java-control-plane +TODO: to solo-kit/api/envoy + + + +##### Types: + + +- [RetryPolicy](#RetryPolicy) + + + + +##### Source File: [github.com/solo-io/gloo/projects/gloo/api/v1/plugins/retries/retries.proto](https://github.com/solo-io/gloo/blob/master/projects/gloo/api/v1/plugins/retries/retries.proto) + + + + + +--- +### RetryPolicy + + +Retry Policy applied to a route + +```yaml +"retry_on": string +"num_retries": int +"per_try_timeout": .google.protobuf.Duration + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `retry_on` | `string` | Specifies the conditions under which retry takes place. These are the same conditions [documented for Envoy](https://www.envoyproxy.io/docs/envoy/latest/configuration/http_filters/router_filter#config-http-filters-router-x-envoy-retry-on) | | +| `num_retries` | `int` | Specifies the allowed number of retries. This parameter is optional and defaults to 1. These are the same conditions [documented for Envoy](https://www.envoyproxy.io/docs/envoy/latest/configuration/http_filters/router_filter#config-http-filters-router-x-envoy-retry-on) | | +| `per_try_timeout` | [.google.protobuf.Duration](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/duration) | Specifies a non-zero upstream timeout per retry attempt. This parameter is optional. | | + + + + + + + + diff --git a/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/service_spec.proto.sk.md b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/service_spec.proto.sk.md new file mode 100644 index 0000000..300af3e --- /dev/null +++ b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/service_spec.proto.sk.md @@ -0,0 +1,51 @@ + +--- +title: "service_spec.proto" +weight: 5 +--- + + + + +### Package: `plugins.gloo.solo.io` +##### Types: + + +- [ServiceSpec](#ServiceSpec) + + + + +##### Source File: [github.com/solo-io/gloo/projects/gloo/api/v1/plugins/service_spec.proto](https://github.com/solo-io/gloo/blob/master/projects/gloo/api/v1/plugins/service_spec.proto) + + + + + +--- +### ServiceSpec + + +Describes APIs and application-level information for services +Gloo routes to. ServiceSpec is contained within the UpstreamSpec for certain types +of upstreams, including Kubernetes, Consul, and Static. +ServiceSpec configuration is opaque to Gloo and handled by Service Plugins. + +```yaml +"rest": .rest.plugins.gloo.solo.io.ServiceSpec +"grpc": .grpc.plugins.gloo.solo.io.ServiceSpec + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `rest` | [.rest.plugins.gloo.solo.io.ServiceSpec](../rest/rest.proto.sk#ServiceSpec) | | | +| `grpc` | [.grpc.plugins.gloo.solo.io.ServiceSpec](../grpc/grpc.proto.sk#ServiceSpec) | | | + + + + + + + + diff --git a/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/static/static.proto.sk.md b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/static/static.proto.sk.md new file mode 100644 index 0000000..542f8f5 --- /dev/null +++ b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/static/static.proto.sk.md @@ -0,0 +1,76 @@ + +--- +title: "static.proto" +weight: 5 +--- + + + + +### Package: `static.plugins.gloo.solo.io` +##### Types: + + +- [UpstreamSpec](#UpstreamSpec) +- [Host](#Host) + + + + +##### Source File: [github.com/solo-io/gloo/projects/gloo/api/v1/plugins/static/static.proto](https://github.com/solo-io/gloo/blob/master/projects/gloo/api/v1/plugins/static/static.proto) + + + + + +--- +### UpstreamSpec + + +Static upstreams are used to route request to services listening at fixed IP/Addresses. +Static upstreams can be used to proxy any kind of service, and therefore contain a ServiceSpec +for additional service-specific configuration. +Unlike upstreams created by service discovery, Static Upstreams must be created manually by users + +```yaml +"hosts": []static.plugins.gloo.solo.io.Host +"use_tls": bool +"service_spec": .plugins.gloo.solo.io.ServiceSpec +"use_http2": bool + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `hosts` | [[]static.plugins.gloo.solo.io.Host](../static.proto.sk#Host) | A list of addresses and ports at least one must be specified | | +| `use_tls` | `bool` | Attempt to use outbound TLS Gloo will automatically set this to true for port 443 | | +| `service_spec` | [.plugins.gloo.solo.io.ServiceSpec](../../service_spec.proto.sk#ServiceSpec) | An optional Service Spec describing the service listening at this address | | +| `use_http2` | `bool` | Use http2 when communicating with this upstream | | + + + + +--- +### Host + + +Represents a single instance of an upstream + +```yaml +"addr": string +"port": int + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `addr` | `string` | Address (hostname or IP) | | +| `port` | `int` | Port the instance is listening on | | + + + + + + + + diff --git a/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/transformation/parameters.proto.sk.md b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/transformation/parameters.proto.sk.md new file mode 100644 index 0000000..773b790 --- /dev/null +++ b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/transformation/parameters.proto.sk.md @@ -0,0 +1,47 @@ + +--- +title: "parameters.proto" +weight: 5 +--- + + + + +### Package: `transformation.plugins.gloo.solo.io` +##### Types: + + +- [Parameters](#Parameters) + + + + +##### Source File: [github.com/solo-io/gloo/projects/gloo/api/v1/plugins/transformation/parameters.proto](https://github.com/solo-io/gloo/blob/master/projects/gloo/api/v1/plugins/transformation/parameters.proto) + + + + + +--- +### Parameters + + + +```yaml +"headers": map +"path": .google.protobuf.StringValue + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `headers` | `map` | headers that will be used to extract data for processing output templates Gloo will search for parameters by their name in header value strings, enclosed in single curly braces Example: extensions: parameters: headers: x-user-id: { userId } | | +| `path` | [.google.protobuf.StringValue](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/string-value) | part of the (or the entire) path that will be used extract data for processing output templates Gloo will search for parameters by their name in header value strings, enclosed in single curly braces Example: extensions: parameters: path: /users/{ userId } | | + + + + + + + + diff --git a/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/transformation/prefix_rewrite.proto.sk.md b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/transformation/prefix_rewrite.proto.sk.md new file mode 100644 index 0000000..93a8894 --- /dev/null +++ b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/transformation/prefix_rewrite.proto.sk.md @@ -0,0 +1,52 @@ + +--- +title: "prefix_rewrite.proto" +weight: 5 +--- + + + + +### Package: `transformation.plugins.gloo.solo.io` +TODO: this was copied form the transformation filter. +TODO: instead of manually copying, we want to do it via script, similar to the java-control-plane +TODO: to solo-kit/api/envoy + + + +##### Types: + + +- [PrefixRewrite](#PrefixRewrite) + + + + +##### Source File: [github.com/solo-io/gloo/projects/gloo/api/v1/plugins/transformation/prefix_rewrite.proto](https://github.com/solo-io/gloo/blob/master/projects/gloo/api/v1/plugins/transformation/prefix_rewrite.proto) + + + + + +--- +### PrefixRewrite + + +if set, prefix_rewrite will be used to rewrite the matched HTTP Path prefix on requests to this value. + +```yaml +"prefix_rewrite": string + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `prefix_rewrite` | `string` | Set to an empty string to remove the matched HTTP Path prefix | | + + + + + + + + diff --git a/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/transformation/transformation.proto.sk.md b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/transformation/transformation.proto.sk.md new file mode 100644 index 0000000..c15e2b7 --- /dev/null +++ b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/plugins/transformation/transformation.proto.sk.md @@ -0,0 +1,193 @@ + +--- +title: "transformation.proto" +weight: 5 +--- + + + + +### Package: `transformation.plugins.gloo.solo.io` +TODO: this was copied form the transformation filter. +TODO: instead of manually copying, we want to do it via script, similar to the java-control-plane +TODO: to solo-kit/api/envoy + + + +##### Types: + + +- [RouteTransformations](#RouteTransformations) +- [Transformation](#Transformation) +- [Extraction](#Extraction) +- [TransformationTemplate](#TransformationTemplate) +- [InjaTemplate](#InjaTemplate) +- [Passthrough](#Passthrough) +- [MergeExtractorsToBody](#MergeExtractorsToBody) +- [HeaderBodyTransform](#HeaderBodyTransform) + + + + +##### Source File: [github.com/solo-io/gloo/projects/gloo/api/v1/plugins/transformation/transformation.proto](https://github.com/solo-io/gloo/blob/master/projects/gloo/api/v1/plugins/transformation/transformation.proto) + + + + + +--- +### RouteTransformations + + + +```yaml +"request_transformation": .transformation.plugins.gloo.solo.io.Transformation +"response_transformation": .transformation.plugins.gloo.solo.io.Transformation + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `request_transformation` | [.transformation.plugins.gloo.solo.io.Transformation](../transformation.proto.sk#Transformation) | | | +| `response_transformation` | [.transformation.plugins.gloo.solo.io.Transformation](../transformation.proto.sk#Transformation) | | | + + + + +--- +### Transformation + + +[#proto-status: experimental] + +```yaml +"transformation_template": .transformation.plugins.gloo.solo.io.TransformationTemplate +"header_body_transform": .transformation.plugins.gloo.solo.io.HeaderBodyTransform + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `transformation_template` | [.transformation.plugins.gloo.solo.io.TransformationTemplate](../transformation.proto.sk#TransformationTemplate) | | | +| `header_body_transform` | [.transformation.plugins.gloo.solo.io.HeaderBodyTransform](../transformation.proto.sk#HeaderBodyTransform) | | | + + + + +--- +### Extraction + + + +```yaml +"header": string +"regex": string +"subgroup": int + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `header` | `string` | | | +| `regex` | `string` | what information to extract. if extraction fails the result is an empty value. | | +| `subgroup` | `int` | | | + + + + +--- +### TransformationTemplate + + + +```yaml +"advanced_templates": bool +"extractors": map +"headers": map +"body": .transformation.plugins.gloo.solo.io.InjaTemplate +"passthrough": .transformation.plugins.gloo.solo.io.Passthrough +"merge_extractors_to_body": .transformation.plugins.gloo.solo.io.MergeExtractorsToBody + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `advanced_templates` | `bool` | | | +| `extractors` | `map` | Extractors are in the origin request language domain | | +| `headers` | `map` | | | +| `body` | [.transformation.plugins.gloo.solo.io.InjaTemplate](../transformation.proto.sk#InjaTemplate) | | | +| `passthrough` | [.transformation.plugins.gloo.solo.io.Passthrough](../transformation.proto.sk#Passthrough) | | | +| `merge_extractors_to_body` | [.transformation.plugins.gloo.solo.io.MergeExtractorsToBody](../transformation.proto.sk#MergeExtractorsToBody) | | | + + + + +--- +### InjaTemplate + + +custom functions: +header_value(name) -> from the original headers +extracted_value(name, index) -> from the extracted values + +```yaml +"text": string + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `text` | `string` | | | + + + + +--- +### Passthrough + + + +```yaml + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | + + + + +--- +### MergeExtractorsToBody + + + +```yaml + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | + + + + +--- +### HeaderBodyTransform + + + +```yaml + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | + + + + + + + + diff --git a/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/proxy.proto.sk.md b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/proxy.proto.sk.md new file mode 100644 index 0000000..dc35386 --- /dev/null +++ b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/proxy.proto.sk.md @@ -0,0 +1,402 @@ + +--- +title: "proxy.proto" +weight: 5 +--- + + + + +### Package: `gloo.solo.io` +##### Types: + + +- [Proxy](#Proxy) **Top-Level Resource** +- [Listener](#Listener) +- [HttpListener](#HttpListener) +- [VirtualHost](#VirtualHost) +- [Route](#Route) +- [Matcher](#Matcher) +- [HeaderMatcher](#HeaderMatcher) +- [QueryParameterMatcher](#QueryParameterMatcher) +- [RouteAction](#RouteAction) +- [Destination](#Destination) +- [MultiDestination](#MultiDestination) +- [WeightedDestination](#WeightedDestination) +- [RedirectAction](#RedirectAction) +- [RedirectResponseCode](#RedirectResponseCode) +- [DirectResponseAction](#DirectResponseAction) + + + + +##### Source File: [github.com/solo-io/gloo/projects/gloo/api/v1/proxy.proto](https://github.com/solo-io/gloo/blob/master/projects/gloo/api/v1/proxy.proto) + + + + + +--- +### Proxy + + + +A Proxy is a container for the entire set of configuration that will to be applied to one or more Proxy instances. +Proxies can be understood as a set of listeners, represents a different bind address/port where the proxy will listen +for connections. Each listener has its own set of configuration. + +If any of the sub-resources within a listener is declared invalid (e.g. due to invalid user configuration), the +proxy will be marked invalid by Gloo. + +Proxy instances that register with Gloo are assigned the proxy configuration corresponding with +a proxy-specific identifier. +In the case of Envoy, proxy instances are identified by their Node ID. Node IDs must match a existing Proxy +Node ID can be specified in Envoy with the `--service-node` flag, or in the Envoy instance's bootstrap config. + +```yaml +"listeners": []gloo.solo.io.Listener +"status": .core.solo.io.Status +"metadata": .core.solo.io.Metadata + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `listeners` | [[]gloo.solo.io.Listener](../proxy.proto.sk#Listener) | Define here each listener the proxy should create. Listeners define the a set of behaviors for a single bind address/port where the proxy will listen If no listeners are specified, the instances configured with the proxy resource will not accept connections. | | +| `status` | [.core.solo.io.Status](../../../../../../solo-kit/api/v1/status.proto.sk#Status) | Status indicates the validation status of this resource. Status is read-only by clients, and set by gloo during validation | | +| `metadata` | [.core.solo.io.Metadata](../../../../../../solo-kit/api/v1/metadata.proto.sk#Metadata) | Metadata contains the object metadata for this resource | | + + + + +--- +### Listener + + +Listeners define the address:port where the proxy will listen for incoming connections +A Listener accepts connections (currently only HTTP is supported) and apply user-defined behavior for those connections, +e.g. performing SSL termination, HTTP retries, and rate limiting. + +```yaml +"name": string +"bind_address": string +"bind_port": int +"http_listener": .gloo.solo.io.HttpListener +"ssl_configuations": []gloo.solo.io.SslConfig + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `name` | `string` | the name of the listener. names must be unique for each listener within a proxy | | +| `bind_address` | `string` | the bind address for the listener. both ipv4 and ipv6 formats are supported | | +| `bind_port` | `int` | the port to bind on ports numbers must be unique for listeners within a proxy | | +| `http_listener` | [.gloo.solo.io.HttpListener](../proxy.proto.sk#HttpListener) | The HTTP Listener is currently the only supported listener type. It contains configuration options for GLoo's HTTP-level features including request-based routing | | +| `ssl_configuations` | [[]gloo.solo.io.SslConfig](../ssl.proto.sk#SslConfig) | SSL Config is optional for the listener. If provided, the listener will serve TLS for connections on this port Multiple SslConfigs are supported for the pupose of SNI. Be aware that the SNI domain provided in the SSL Config must match a domain in virtual host TODO(ilackarms): ensure that ssl configs without a matching virtual host are errored | | + + + + +--- +### HttpListener + + +Use this listener to configure proxy behavior for any HTTP-level features including defining routes (via virtualservices). +HttpListeners also contain plugin configuration that applies globally across all virtaul hosts on the listener. +Some plugins can be configured to work both on the listener and virtual host level (such as the rate limit plugin) + +```yaml +"virtual_hosts": []gloo.solo.io.VirtualHost +"listener_plugins": .gloo.solo.io.ListenerPlugins + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `virtual_hosts` | [[]gloo.solo.io.VirtualHost](../proxy.proto.sk#VirtualHost) | the set of virtual hosts that will be accessible by clients connecting to this listener. at least one virtual host must be specified for this listener to be active (else connections will be refused) the set of domains for each virtual host must be unique, or the config will be considered invalid | | +| `listener_plugins` | [.gloo.solo.io.ListenerPlugins](../plugins.proto.sk#ListenerPlugins) | Plugins contains top-level plugin configuration to be applied to a listener Listener config is applied to all HTTP traffic that connects to this listener. Some configuration here can be overridden in Virtual Host Plugin configuration or Route Plugin configuration Plugins should be specified here in the form of `"plugin_name": {..//plugin_config...}` to allow specifying multiple plugins. | | + + + + +--- +### VirtualHost + + +Virtual Hosts group an ordered list of routes under one or more domains. +Each Virtual Host has a logical name, which must be unique for the listener. +An HTTP request is first matched to a virtual host based on its host header, then to a route within the virtual host. +If a request is not matched to any virtual host or a route therein, the target proxy will reply with a 404. + +```yaml +"name": string +"domains": []string +"routes": []gloo.solo.io.Route +"virtual_host_plugins": .gloo.solo.io.VirtualHostPlugins + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `name` | `string` | the logical name of the virtual host. names must be unique for each virtual host within a listener | | +| `domains` | `[]string` | The list of domains (i.e.: matching the `Host` header of a request) that belong to this virtual host. Note that the wildcard will not match the empty string. e.g. “*-bar.foo.com” will match “baz-bar.foo.com” but not “-bar.foo.com”. Additionally, a special entry “*” is allowed which will match any host/authority header. Only a single virtual host in the entire route configuration can match on “*”. A domain must be unique across all virtual hosts or the config will be invalidated by Gloo Domains on virtual hosts obey the same rules as [Envoy Virtual Hosts](https://github.com/envoyproxy/envoy/blob/master/api/envoy/api/v2/route/route.proto) | | +| `routes` | [[]gloo.solo.io.Route](../proxy.proto.sk#Route) | The list of HTTP routes define routing actions to be taken for incoming HTTP requests whose host header matches this virtual host. If the request matches more than one route in the list, the first route matched will be selected. If the list of routes is empty, the virtual host will be ignored by Gloo. | | +| `virtual_host_plugins` | [.gloo.solo.io.VirtualHostPlugins](../plugins.proto.sk#VirtualHostPlugins) | Plugins contains top-level plugin configuration to be applied to a listener Listener config is applied to all HTTP traffic that connects to this listener. Some configuration here can be overridden in Virtual Host Plugin configuration or Route Plugin configuration Plugins should be specified here in the form of `"plugin_name": {..//plugin_config...}` to allow specifying multiple plugins. | | + + + + +--- +### Route + + +* +Routes declare the entrypoints on virtual hosts and the action to take for matched requests. + +```yaml +"matcher": .gloo.solo.io.Matcher +"route_action": .gloo.solo.io.RouteAction +"redirect_action": .gloo.solo.io.RedirectAction +"direct_response_action": .gloo.solo.io.DirectResponseAction +"route_plugins": .gloo.solo.io.RoutePlugins + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `matcher` | [.gloo.solo.io.Matcher](../proxy.proto.sk#Matcher) | The matcher contains parameters for matching requests (i.e.: based on HTTP path, headers, etc.) | | +| `route_action` | [.gloo.solo.io.RouteAction](../proxy.proto.sk#RouteAction) | This action is the primary action to be selected for most routes. The RouteAction tells the proxy to route requests to an upstream. | | +| `redirect_action` | [.gloo.solo.io.RedirectAction](../proxy.proto.sk#RedirectAction) | Redirect actions tell the proxy to return a redirect response to the downstream client | | +| `direct_response_action` | [.gloo.solo.io.DirectResponseAction](../proxy.proto.sk#DirectResponseAction) | Return an arbitrary HTTP response directly, without proxying. | | +| `route_plugins` | [.gloo.solo.io.RoutePlugins](../plugins.proto.sk#RoutePlugins) | Route Plugins extend the behavior of routes. Route plugins include configuration such as retries, rate limiting, and request/resonse transformation. Plugins should be specified here in the form of `"plugin_name": {..//plugin_config...}` to allow specifying multiple plugins. | | + + + + +--- +### Matcher + + +Parameters for matching routes to requests received by a Gloo-managed proxy + +```yaml +"prefix": string +"exact": string +"regex": string +"headers": []gloo.solo.io.HeaderMatcher +"query_parameters": []gloo.solo.io.QueryParameterMatcher +"methods": []string + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `prefix` | `string` | If specified, the route is a prefix rule meaning that the prefix must match the beginning of the *:path* header. | | +| `exact` | `string` | If specified, the route is an exact path rule meaning that the path must exactly match the *:path* header once the query string is removed. | | +| `regex` | `string` | If specified, the route is a regular expression rule meaning that the regex must match the *:path* header once the query string is removed. The entire path (without the query string) must match the regex. The rule will not match if only a subsequence of the *:path* header matches the regex. The regex grammar is defined `here `_. Examples: * The regex */b[io]t* matches the path */bit* * The regex */b[io]t* matches the path */bot* * The regex */b[io]t* does not match the path */bite* * The regex */b[io]t* does not match the path */bit/bot* | | +| `headers` | [[]gloo.solo.io.HeaderMatcher](../proxy.proto.sk#HeaderMatcher) | Specifies a set of headers that the route should match on. The router will check the request’s headers against all the specified headers in the route config. A match will happen if all the headers in the route are present in the request with the same values (or based on presence if the value field is not in the config). | | +| `query_parameters` | [[]gloo.solo.io.QueryParameterMatcher](../proxy.proto.sk#QueryParameterMatcher) | Specifies a set of URL query parameters on which the route should match. The router will check the query string from the *path* header against all the specified query parameters. If the number of specified query parameters is nonzero, they all must match the *path* header's query string for a match to occur. | | +| `methods` | `[]string` | HTTP Method/Verb(s) to match on. If none specified, the matcher will ignore the HTTP Method | | + + + + +--- +### HeaderMatcher + + +Internally, Gloo always uses the HTTP/2 *:authority* header to represent the HTTP/1 *Host* + header. Thus, if attempting to match on *Host*, match on *:authority* instead. + + In the absence of any header match specifier, match will default to `present_match` + i.e, a request that has the `name` header will match, regardless of the header's + value. + +```yaml +"name": string +"value": string +"regex": bool + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `name` | `string` | Specifies the name of the header in the request. | | +| `value` | `string` | Specifies the value of the header. If the value is absent a request that has the name header will match, regardless of the header’s value. | | +| `regex` | `bool` | Specifies whether the header value should be treated as regex or not. | | + + + + +--- +### QueryParameterMatcher + + +Query parameter matching treats the query string of a request's :path header +as an ampersand-separated list of keys and/or key=value elements. + +```yaml +"name": string +"value": string +"regex": bool + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `name` | `string` | Specifies the name of a key that must be present in the requested *path*'s query string. | | +| `value` | `string` | Specifies the value of the key. If the value is absent, a request that contains the key in its query string will match, whether the key appears with a value (e.g., "?debug=true") or not (e.g., "?debug") | | +| `regex` | `bool` | Specifies whether the query parameter value is a regular expression. Defaults to false. The entire query parameter value (i.e., the part to the right of the equals sign in "key=value") must match the regex. E.g., the regex "\d+$" will match "123" but not "a123" or "123a". | | + + + + +--- +### RouteAction + + +RouteActions are used to route matched requests to upstreams. + +```yaml +"single": .gloo.solo.io.Destination +"multi": .gloo.solo.io.MultiDestination + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `single` | [.gloo.solo.io.Destination](../proxy.proto.sk#Destination) | Use SingleDestination to route to a single upstream | | +| `multi` | [.gloo.solo.io.MultiDestination](../proxy.proto.sk#MultiDestination) | Use MultiDestination to load balance requests between multiple upstreams (by weight) | | + + + + +--- +### Destination + + +Destinations define routable destinations for proxied requests + +```yaml +"upstream": .core.solo.io.ResourceRef +"destination_spec": .gloo.solo.io.DestinationSpec + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `upstream` | [.core.solo.io.ResourceRef](../../../../../../solo-kit/api/v1/ref.proto.sk#ResourceRef) | The upstream to route requests to | | +| `destination_spec` | [.gloo.solo.io.DestinationSpec](../plugins.proto.sk#DestinationSpec) | Some upstreams utilize plugins which require or permit additional configuration on routes targeting them. gRPC upstreams, for example, allow specifying REST-style parameters for JSON-to-gRPC transcoding in the destination config. If the destination config is required for the upstream and not provided by the user, Gloo will invalidate the destination and its parent resources. | | + + + + +--- +### MultiDestination + + +MultiDestination is a container for a set of weighted destinations. Gloo will load balance traffic for a single +route across multiple destinations according to their specified weights. + +```yaml +"destinations": []gloo.solo.io.WeightedDestination + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `destinations` | [[]gloo.solo.io.WeightedDestination](../proxy.proto.sk#WeightedDestination) | This list must contain at least one destination or the listener housing this route will be invalid, causing Gloo to error the parent proxy resource. | | + + + + +--- +### WeightedDestination + + +WeightedDestination attaches a weight to a single destination. + +```yaml +"destination": .gloo.solo.io.Destination +"weight": int + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `destination` | [.gloo.solo.io.Destination](../proxy.proto.sk#Destination) | | | +| `weight` | `int` | Weight must be greater than zero Routing to each destination will be balanced by the ratio of the destination's weight to the total weight on a route | | + + + + +--- +### RedirectAction + + +TODO(ilackarms): evaluate how much to differentiate (or if even to include) RedirectAction +Notice: RedirectAction is copioed directly from https://github.com/envoyproxy/envoy/blob/master/api/envoy/api/v2/route/route.proto + +```yaml +"host_redirect": string +"path_redirect": string +"prefix_rewrite": string +"response_code": .gloo.solo.io.RedirectAction.RedirectResponseCode +"https_redirect": bool +"strip_query": bool + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `host_redirect` | `string` | The host portion of the URL will be swapped with this value. | | +| `path_redirect` | `string` | The path portion of the URL will be swapped with this value. | | +| `prefix_rewrite` | `string` | Indicates that during redirection, the matched prefix (or path) should be swapped with this value. This option allows redirect URLs be dynamically created based on the request. Pay attention to the use of trailing slashes as mentioned in `RouteAction`'s `prefix_rewrite`. | | +| `response_code` | [.gloo.solo.io.RedirectAction.RedirectResponseCode](../proxy.proto.sk#RedirectResponseCode) | The HTTP status code to use in the redirect response. The default response code is MOVED_PERMANENTLY (301). | | +| `https_redirect` | `bool` | The scheme portion of the URL will be swapped with "https". | | +| `strip_query` | `bool` | Indicates that during redirection, the query portion of the URL will be removed. Default value is false. | | + + + + +--- +### RedirectResponseCode + + + +| Name | Description | +| ----- | ----------- | +| `MOVED_PERMANENTLY` | Moved Permanently HTTP Status Code - 301. | +| `FOUND` | Found HTTP Status Code - 302. | +| `SEE_OTHER` | See Other HTTP Status Code - 303. | +| `TEMPORARY_REDIRECT` | Temporary Redirect HTTP Status Code - 307. | +| `PERMANENT_REDIRECT` | Permanent Redirect HTTP Status Code - 308. | + + + + +--- +### DirectResponseAction + + +TODO(ilackarms): evaluate how much to differentiate (or if even to include) DirectResponseAction +DirectResponseAction is copied directly from https://github.com/envoyproxy/envoy/blob/master/api/envoy/api/v2/route/route.proto + +```yaml +"status": int +"body": string + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `status` | `int` | Specifies the HTTP response status to be returned. | | +| `body` | `string` | Specifies the content of the response body. If this setting is omitted, no body is included in the generated response. Note: Headers can be specified using the Header Modification plugin in the enclosing Route, Virtual Host, or Listener. | | + + + + + + + + diff --git a/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/ssl.proto.sk.md b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/ssl.proto.sk.md new file mode 100644 index 0000000..b2c44dc --- /dev/null +++ b/docs/v1/github.com/solo-io/gloo/projects/gloo/api/v1/ssl.proto.sk.md @@ -0,0 +1,96 @@ + +--- +title: "ssl.proto" +weight: 5 +--- + + + + +### Package: `gloo.solo.io` +##### Types: + + +- [SslConfig](#SslConfig) +- [SSLFiles](#SSLFiles) +- [UpstreamSslConfig](#UpstreamSslConfig) + + + + +##### Source File: [github.com/solo-io/gloo/projects/gloo/api/v1/ssl.proto](https://github.com/solo-io/gloo/blob/master/projects/gloo/api/v1/ssl.proto) + + + + + +--- +### SslConfig + + +SslConfig contains the options necessary to configure a virtual host or listener to use TLS + +```yaml +"secret_ref": .core.solo.io.ResourceRef +"ssl_files": .gloo.solo.io.SSLFiles +"sni_domains": []string + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `secret_ref` | [.core.solo.io.ResourceRef](../../../../../../solo-kit/api/v1/ref.proto.sk#ResourceRef) | * SecretRef contains the secret ref to a gloo secret containing the following structure: { "tls.crt": , "tls.key": } | | +| `ssl_files` | [.gloo.solo.io.SSLFiles](../ssl.proto.sk#SSLFiles) | SSLFiles reference paths to certificates which are local to the proxy | | +| `sni_domains` | `[]string` | optional. the SNI domains that should be considered for TLS connections | | + + + + +--- +### SSLFiles + + +SSLFiles reference paths to certificates which can be read by the proxy off of its local filesystem + +```yaml +"tls_cert": string +"tls_key": string +"root_ca": string + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `tls_cert` | `string` | | | +| `tls_key` | `string` | | | +| `root_ca` | `string` | for client cert validation. optional | | + + + + +--- +### UpstreamSslConfig + + +SslConfig contains the options necessary to configure a virtual host or listener to use TLS + +```yaml +"secret_ref": .core.solo.io.ResourceRef +"ssl_files": .gloo.solo.io.SSLFiles +"sni": string + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `secret_ref` | [.core.solo.io.ResourceRef](../../../../../../solo-kit/api/v1/ref.proto.sk#ResourceRef) | | | +| `ssl_files` | [.gloo.solo.io.SSLFiles](../ssl.proto.sk#SSLFiles) | SSLFiles reference paths to certificates which are local to the proxy | | +| `sni` | `string` | optional. the SNI domains that should be considered for TLS connections | | + + + + + + + + diff --git a/docs/v1/github.com/solo-io/solo-kit/api/v1/metadata.proto.sk.md b/docs/v1/github.com/solo-io/solo-kit/api/v1/metadata.proto.sk.md new file mode 100644 index 0000000..4aaf673 --- /dev/null +++ b/docs/v1/github.com/solo-io/solo-kit/api/v1/metadata.proto.sk.md @@ -0,0 +1,55 @@ + +--- +title: "metadata.proto" +weight: 5 +--- + + + + +### Package: `core.solo.io` +##### Types: + + +- [Metadata](#Metadata) + + + + +##### Source File: [github.com/solo-io/solo-kit/api/v1/metadata.proto](https://github.com/solo-io/solo-kit/blob/master/api/v1/metadata.proto) + + + + + +--- +### Metadata + + +* +Metadata contains general properties of resources for purposes of versioning, annotating, and namespacing. + +```yaml +"name": string +"namespace": string +"resource_version": string +"labels": map +"annotations": map + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `name` | `string` | Name of the resource. Names must be unique and follow the following syntax rules: One or more lowercase rfc1035/rfc1123 labels separated by '.' with a maximum length of 253 characters. | | +| `namespace` | `string` | Namespace is used for the namespacing of resources. | | +| `resource_version` | `string` | An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. | | +| `labels` | `map` | Map of string keys and values that can be used to organize and categorize (scope and select) objects. Some resources contain `selectors` which can be linked with other resources by their labels | | +| `annotations` | `map` | Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. | | + + + + + + + + diff --git a/docs/v1/github.com/solo-io/solo-kit/api/v1/ref.proto.sk.md b/docs/v1/github.com/solo-io/solo-kit/api/v1/ref.proto.sk.md new file mode 100644 index 0000000..a318a00 --- /dev/null +++ b/docs/v1/github.com/solo-io/solo-kit/api/v1/ref.proto.sk.md @@ -0,0 +1,49 @@ + +--- +title: "ref.proto" +weight: 5 +--- + + + + +### Package: `core.solo.io` +##### Types: + + +- [ResourceRef](#ResourceRef) + + + + +##### Source File: [github.com/solo-io/solo-kit/api/v1/ref.proto](https://github.com/solo-io/solo-kit/blob/master/api/v1/ref.proto) + + + + + +--- +### ResourceRef + + +A way to reference resources across namespaces +TODO(ilackarms): make upstreamname and secretref into ResourceRefs + +```yaml +"name": string +"namespace": string + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `name` | `string` | | | +| `namespace` | `string` | | | + + + + + + + + diff --git a/docs/v1/github.com/solo-io/solo-kit/api/v1/solo-kit.proto.sk.md b/docs/v1/github.com/solo-io/solo-kit/api/v1/solo-kit.proto.sk.md new file mode 100644 index 0000000..876bd97 --- /dev/null +++ b/docs/v1/github.com/solo-io/solo-kit/api/v1/solo-kit.proto.sk.md @@ -0,0 +1,51 @@ + +--- +title: "solo-kit.proto" +weight: 5 +--- + + + + +### Package: `core.solo.io` +##### Types: + + +- [Resource](#Resource) + + + + +##### Source File: [github.com/solo-io/solo-kit/api/v1/solo-kit.proto](https://github.com/solo-io/solo-kit/blob/master/api/v1/solo-kit.proto) + + + + + +--- +### Resource + + + +```yaml +"short_name": string +"plural_name": string +"cluster_scoped": bool +"skip_docs_gen": bool + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `short_name` | `string` | becomes the kubernetes short name for the generated crd | | +| `plural_name` | `string` | becomes the kubernetes plural name for the generated crd | | +| `cluster_scoped` | `bool` | the resource lives at the cluster level, namespace is ignored by the server | | +| `skip_docs_gen` | `bool` | indicates whether documentation generation has to be skipped for the given resource, defaults to false | | + + + + + + + + diff --git a/docs/v1/github.com/solo-io/solo-kit/api/v1/status.proto.sk.md b/docs/v1/github.com/solo-io/solo-kit/api/v1/status.proto.sk.md new file mode 100644 index 0000000..40359d4 --- /dev/null +++ b/docs/v1/github.com/solo-io/solo-kit/api/v1/status.proto.sk.md @@ -0,0 +1,69 @@ + +--- +title: "status.proto" +weight: 5 +--- + + + + +### Package: `core.solo.io` +##### Types: + + +- [Status](#Status) +- [State](#State) + + + + +##### Source File: [github.com/solo-io/solo-kit/api/v1/status.proto](https://github.com/solo-io/solo-kit/blob/master/api/v1/status.proto) + + + + + +--- +### Status + + +* +Status indicates whether a resource has been (in)validated by a reporter in the system. +Statuses are meant to be read-only by users + +```yaml +"state": .core.solo.io.Status.State +"reason": string +"reported_by": string +"subresource_statuses": map + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `state` | [.core.solo.io.Status.State](../status.proto.sk#State) | State is the enum indicating the state of the resource | | +| `reason` | `string` | Reason is a description of the error for Rejected resources. If the resource is pending or accepted, this field will be empty | | +| `reported_by` | `string` | Reference to the reporter who wrote this status | | +| `subresource_statuses` | `map` | Reference to statuses (by resource-ref string: "Kind.Namespace.Name") of subresources of the parent resource | | + + + + +--- +### State + + + +| Name | Description | +| ----- | ----------- | +| `Pending` | Pending status indicates the resource has not yet been validated | +| `Accepted` | Accepted indicates the resource has been validated | +| `Rejected` | Rejected indicates an invalid configuration by the user Rejected resources may be propagated to the xDS server depending on their severity | + + + + + + + + diff --git a/docs/v1/github.com/solo-io/sqoop/api/v1/resolver_map.proto.sk.md b/docs/v1/github.com/solo-io/sqoop/api/v1/resolver_map.proto.sk.md new file mode 100644 index 0000000..e700173 --- /dev/null +++ b/docs/v1/github.com/solo-io/sqoop/api/v1/resolver_map.proto.sk.md @@ -0,0 +1,201 @@ + +--- +title: "resolver_map.proto" +weight: 5 +--- + + + + +### Package: `sqoop.solo.io` +##### Types: + + +- [ResolverMap](#ResolverMap) **Top-Level Resource** +- [TypeResolver](#TypeResolver) +- [FieldResolver](#FieldResolver) +- [GlooResolver](#GlooResolver) +- [RequestTemplate](#RequestTemplate) +- [ResponseTemplate](#ResponseTemplate) +- [TemplateResolver](#TemplateResolver) +- [NodeJSResolver](#NodeJSResolver) + + + + +##### Source File: [github.com/solo-io/sqoop/api/v1/resolver_map.proto](https://github.com/solo-io/sqoop/blob/master/api/v1/resolver_map.proto) + + + + + +--- +### ResolverMap + + +The ResolverMap object maps Resolvers to the fields in the GraphQL Schema +The skeleton of a Resolver Map will be generated by Sqoop automatically when a schema +is read or updated if one does not alreay exist. + +```yaml +"types": map +"status": .core.solo.io.Status +"metadata": .core.solo.io.Metadata + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `types` | `map` | Types is a map of Type Names (defined in the schema) to a TypeResolver, which contain resolvers for the specific fields of the type | | +| `status` | [.core.solo.io.Status](../../../../solo-kit/api/v1/status.proto.sk#Status) | Status indicates the validation status of this resource. Status is read-only by clients, and set by gloo during validation | | +| `metadata` | [.core.solo.io.Metadata](../../../../solo-kit/api/v1/metadata.proto.sk#Metadata) | Metadata contains the object metadata for this resource | | + + + + +--- +### TypeResolver + + +TypeResolver contains the individual resolvers for each field for a specific type + +```yaml +"fields": map + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `fields` | `map` | This is a map of Field Names to the resolver that Sqoop should invoke when a query arrives for that field | | + + + + +--- +### FieldResolver + + +Resolvers define the actual logic Sqoop needs to know in order to resolve a specific field query + +```yaml +"gloo_resolver": .sqoop.solo.io.GlooResolver +"template_resolver": .sqoop.solo.io.TemplateResolver +"nodejs_resolver": .sqoop.solo.io.NodeJSResolver + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `gloo_resolver` | [.sqoop.solo.io.GlooResolver](../resolver_map.proto.sk#GlooResolver) | a GlooResolver, which leverages Gloo to retrieve data from backend services and functions for the query | | +| `template_resolver` | [.sqoop.solo.io.TemplateResolver](../resolver_map.proto.sk#TemplateResolver) | a TemplateResolver, which uses Go Templates to generate data for the query | | +| `nodejs_resolver` | [.sqoop.solo.io.NodeJSResolver](../resolver_map.proto.sk#NodeJSResolver) | a NodeJSResolver, which calls NodeJS functions to return data for the query | | + + + + +--- +### GlooResolver + + +GlooResolvers are the "meat" of Sqoop. GlooResolvers tell Sqoop how to invoke a "Gloo Function" + +```yaml +"request_template": .sqoop.solo.io.RequestTemplate +"response_template": .sqoop.solo.io.ResponseTemplate +"action": .gloo.solo.io.RouteAction + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `request_template` | [.sqoop.solo.io.RequestTemplate](../resolver_map.proto.sk#RequestTemplate) | the Request Template, if specified, will become the body of the HTTP request used to invoke a function through Gloo input parameters, if needed, should be specified in the request template. See Sqoop's [Resolver documentation] for more information on Request Templates. | | +| `response_template` | [.sqoop.solo.io.ResponseTemplate](../resolver_map.proto.sk#ResponseTemplate) | The response template, if specified, will transform the body of HTTP responses returned by Gloo functions. This field should be used if the object returned by the Gloo Function does not match the type specified in the GraphQL schema. It can also be used to modify or transform responses from their original state. See Sqoop's [Resolver documentation] for more information on Response Templates. | | +| `action` | [.gloo.solo.io.RouteAction](../../../../gloo/projects/gloo/api/v1/proxy.proto.sk#RouteAction) | the routing action to take when resolver is executed. usually this is a Route destination | | + + + + +--- +### RequestTemplate + + + +```yaml +"verb": string +"path": string +"body": string +"headers": map + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `verb` | `string` | | | +| `path` | `string` | | | +| `body` | `string` | | | +| `headers` | `map` | | | + + + + +--- +### ResponseTemplate + + + +```yaml +"body": string +"headers": map + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `body` | `string` | | | +| `headers` | `map` | | | + + + + +--- +### TemplateResolver + + +A Go-template which will return data for a Resolver without making a function call. Template Resolvers can make use +of Sqoop's builtin template functions as well as the data provided by the Params object to the resolver. +Read more about Templates and Resolvers in Sqoop\'s [Resolver documentation]. + +```yaml +"inline_template": string + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `inline_template` | `string` | the Go template as an inline string | | + + + + +--- +### NodeJSResolver + + +NOTE: currently unsupported + +```yaml +"inline_code": string + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `inline_code` | `string` | | | + + + + + + + + diff --git a/docs/v1/github.com/solo-io/sqoop/api/v1/schema.proto.sk.md b/docs/v1/github.com/solo-io/sqoop/api/v1/schema.proto.sk.md new file mode 100644 index 0000000..7c4e9b6 --- /dev/null +++ b/docs/v1/github.com/solo-io/sqoop/api/v1/schema.proto.sk.md @@ -0,0 +1,53 @@ + +--- +title: "schema.proto" +weight: 5 +--- + + + + +### Package: `sqoop.solo.io` +##### Types: + + +- [Schema](#Schema) **Top-Level Resource** + + + + +##### Source File: [github.com/solo-io/sqoop/api/v1/schema.proto](https://github.com/solo-io/sqoop/blob/master/api/v1/schema.proto) + + + + + +--- +### Schema + + +The Schema object wraps the user's GraphQL Schema, which is stored as an inline string. +The Schema Object contains a Status field which is used by SQooP to validate the user's input schema. + +Schemas are matched to resolver maps in the same namespace with the same name + +```yaml +"inline_schema": string +"status": .core.solo.io.Status +"metadata": .core.solo.io.Metadata + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `inline_schema` | `string` | inline the entire graphql schema as a string here | | +| `status` | [.core.solo.io.Status](../../../../solo-kit/api/v1/status.proto.sk#Status) | Status indicates the validation status of this resource. Status is read-only by clients, and set by gloo during validation | | +| `metadata` | [.core.solo.io.Metadata](../../../../solo-kit/api/v1/metadata.proto.sk#Metadata) | Metadata contains the object metadata for this resource | | + + + + + + + + diff --git a/docs/v1/gogoproto/gogo.proto.sk.md b/docs/v1/gogoproto/gogo.proto.sk.md new file mode 100644 index 0000000..47c3fc0 --- /dev/null +++ b/docs/v1/gogoproto/gogo.proto.sk.md @@ -0,0 +1,51 @@ + +--- +title: "gogo.proto" +weight: 5 +--- + + + + +### Package: `gogoproto` +Protocol Buffers for Go with Gadgets + +Copyright (c) 2013, The GoGo Authors. All rights reserved. +http://github.com/gogo/protobuf + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + + +##### Source File: `gogoproto/gogo.proto` + + + + + + + + + diff --git a/docs/v1/google/protobuf/descriptor.proto.sk.md b/docs/v1/google/protobuf/descriptor.proto.sk.md new file mode 100644 index 0000000..e7f2a8d --- /dev/null +++ b/docs/v1/google/protobuf/descriptor.proto.sk.md @@ -0,0 +1,832 @@ + +--- +title: "descriptor.proto" +weight: 5 +--- + + + + +### Package: `google.protobuf` +Protocol Buffers - Google's data interchange format +Copyright 2008 Google Inc. All rights reserved. +https://developers.google.com/protocol-buffers/ + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +Author: kenton@google.com (Kenton Varda) + Based on original Protocol Buffers design by + Sanjay Ghemawat, Jeff Dean, and others. + +The messages in this file describe the definitions found in .proto files. +A valid .proto file can be translated directly to a FileDescriptorProto +without any other information (e.g. without reading its imports). + + + +##### Types: + + +- [FileDescriptorSet](#FileDescriptorSet) +- [FileDescriptorProto](#FileDescriptorProto) +- [DescriptorProto](#DescriptorProto) +- [ExtensionRange](#ExtensionRange) +- [ReservedRange](#ReservedRange) +- [FieldDescriptorProto](#FieldDescriptorProto) +- [Type](#Type) +- [Label](#Label) +- [OneofDescriptorProto](#OneofDescriptorProto) +- [EnumDescriptorProto](#EnumDescriptorProto) +- [EnumValueDescriptorProto](#EnumValueDescriptorProto) +- [ServiceDescriptorProto](#ServiceDescriptorProto) +- [MethodDescriptorProto](#MethodDescriptorProto) +- [FileOptions](#FileOptions) +- [OptimizeMode](#OptimizeMode) +- [MessageOptions](#MessageOptions) +- [FieldOptions](#FieldOptions) +- [CType](#CType) +- [JSType](#JSType) +- [OneofOptions](#OneofOptions) +- [EnumOptions](#EnumOptions) +- [EnumValueOptions](#EnumValueOptions) +- [ServiceOptions](#ServiceOptions) +- [MethodOptions](#MethodOptions) +- [IdempotencyLevel](#IdempotencyLevel) +- [UninterpretedOption](#UninterpretedOption) +- [NamePart](#NamePart) +- [SourceCodeInfo](#SourceCodeInfo) +- [Location](#Location) +- [GeneratedCodeInfo](#GeneratedCodeInfo) +- [Annotation](#Annotation) + + + + +##### Source File: `google/protobuf/descriptor.proto` + + + + + +--- +### FileDescriptorSet + + +The protocol compiler can output a FileDescriptorSet containing the .proto +files it parses. + +```yaml +"file": []google.protobuf.FileDescriptorProto + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `file` | [[]google.protobuf.FileDescriptorProto](../descriptor.proto.sk#FileDescriptorProto) | | | + + + + +--- +### FileDescriptorProto + + +Describes a complete .proto file. + +```yaml +"name": string +"package": string +"dependency": []string +"public_dependency": []int +"weak_dependency": []int +"message_type": []google.protobuf.DescriptorProto +"enum_type": []google.protobuf.EnumDescriptorProto +"service": []google.protobuf.ServiceDescriptorProto +"extension": []google.protobuf.FieldDescriptorProto +"options": .google.protobuf.FileOptions +"source_code_info": .google.protobuf.SourceCodeInfo +"syntax": string + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `name` | `string` | | | +| `package` | `string` | | | +| `dependency` | `[]string` | Names of files imported by this file. | | +| `public_dependency` | `[]int` | Indexes of the public imported files in the dependency list above. | | +| `weak_dependency` | `[]int` | Indexes of the weak imported files in the dependency list. For Google-internal migration only. Do not use. | | +| `message_type` | [[]google.protobuf.DescriptorProto](../descriptor.proto.sk#DescriptorProto) | All top-level definitions in this file. | | +| `enum_type` | [[]google.protobuf.EnumDescriptorProto](../descriptor.proto.sk#EnumDescriptorProto) | | | +| `service` | [[]google.protobuf.ServiceDescriptorProto](../descriptor.proto.sk#ServiceDescriptorProto) | | | +| `extension` | [[]google.protobuf.FieldDescriptorProto](../descriptor.proto.sk#FieldDescriptorProto) | | | +| `options` | [.google.protobuf.FileOptions](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/file-options) | | | +| `source_code_info` | [.google.protobuf.SourceCodeInfo](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/source-code-info) | This field contains optional information about the original source code. You may safely remove this entire field without harming runtime functionality of the descriptors -- the information is needed only by development tools. | | +| `syntax` | `string` | The syntax of the proto file. The supported values are "proto2" and "proto3". | | + + + + +--- +### DescriptorProto + + +Describes a message type. + +```yaml +"name": string +"field": []google.protobuf.FieldDescriptorProto +"extension": []google.protobuf.FieldDescriptorProto +"nested_type": []google.protobuf.DescriptorProto +"enum_type": []google.protobuf.EnumDescriptorProto +"extension_range": []google.protobuf.DescriptorProto.ExtensionRange +"oneof_decl": []google.protobuf.OneofDescriptorProto +"options": .google.protobuf.MessageOptions +"reserved_range": []google.protobuf.DescriptorProto.ReservedRange +"reserved_name": []string + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `name` | `string` | | | +| `field` | [[]google.protobuf.FieldDescriptorProto](../descriptor.proto.sk#FieldDescriptorProto) | | | +| `extension` | [[]google.protobuf.FieldDescriptorProto](../descriptor.proto.sk#FieldDescriptorProto) | | | +| `nested_type` | [[]google.protobuf.DescriptorProto](../descriptor.proto.sk#DescriptorProto) | | | +| `enum_type` | [[]google.protobuf.EnumDescriptorProto](../descriptor.proto.sk#EnumDescriptorProto) | | | +| `extension_range` | [[]google.protobuf.DescriptorProto.ExtensionRange](../descriptor.proto.sk#ExtensionRange) | | | +| `oneof_decl` | [[]google.protobuf.OneofDescriptorProto](../descriptor.proto.sk#OneofDescriptorProto) | | | +| `options` | [.google.protobuf.MessageOptions](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/message-options) | | | +| `reserved_range` | [[]google.protobuf.DescriptorProto.ReservedRange](../descriptor.proto.sk#ReservedRange) | | | +| `reserved_name` | `[]string` | Reserved field names, which may not be used by fields in the same message. A given name may only be reserved once. | | + + + + +--- +### ExtensionRange + + + +```yaml +"start": int +"end": int + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `start` | `int` | | | +| `end` | `int` | | | + + + + +--- +### ReservedRange + + +Range of reserved tag numbers. Reserved tag numbers may not be used by +fields or extension ranges in the same message. Reserved ranges may +not overlap. + +```yaml +"start": int +"end": int + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `start` | `int` | | | +| `end` | `int` | | | + + + + +--- +### FieldDescriptorProto + + +Describes a field within a message. + +```yaml +"name": string +"number": int +"label": .google.protobuf.FieldDescriptorProto.Label +"type": .google.protobuf.FieldDescriptorProto.Type +"type_name": string +"extendee": string +"default_value": string +"oneof_index": int +"json_name": string +"options": .google.protobuf.FieldOptions + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `name` | `string` | | | +| `number` | `int` | | | +| `label` | [.google.protobuf.FieldDescriptorProto.Label](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/field-descriptor-proto.-label) | | | +| `type` | [.google.protobuf.FieldDescriptorProto.Type](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/field-descriptor-proto.-type) | If type_name is set, this need not be set. If both this and type_name are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP. | | +| `type_name` | `string` | For message and enum types, this is the name of the type. If the name starts with a '.', it is fully-qualified. Otherwise, C++-like scoping rules are used to find the type (i.e. first the nested types within this message are searched, then within the parent, on up to the root namespace). | | +| `extendee` | `string` | For extensions, this is the name of the type being extended. It is resolved in the same manner as type_name. | | +| `default_value` | `string` | For numeric types, contains the original text representation of the value. For booleans, "true" or "false". For strings, contains the default text contents (not escaped in any way). For bytes, contains the C escaped value. All bytes >= 128 are escaped. TODO(kenton): Base-64 encode? | | +| `oneof_index` | `int` | If set, gives the index of a oneof in the containing type's oneof_decl list. This field is a member of that oneof. | | +| `json_name` | `string` | JSON name of this field. The value is set by protocol compiler. If the user has set a "json_name" option on this field, that option's value will be used. Otherwise, it's deduced from the field's name by converting it to camelCase. | | +| `options` | [.google.protobuf.FieldOptions](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/field-options) | | | + + + + +--- +### Type + + + +| Name | Description | +| ----- | ----------- | +| `TYPE_DOUBLE` | 0 is reserved for errors. Order is weird for historical reasons. | +| `TYPE_FLOAT` | | +| `TYPE_INT64` | Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if negative values are likely. | +| `TYPE_UINT64` | | +| `TYPE_INT32` | Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if negative values are likely. | +| `TYPE_FIXED64` | | +| `TYPE_FIXED32` | | +| `TYPE_BOOL` | | +| `TYPE_STRING` | | +| `TYPE_GROUP` | Tag-delimited aggregate. Group type is deprecated and not supported in proto3. However, Proto3 implementations should still be able to parse the group wire format and treat group fields as unknown fields. | +| `TYPE_MESSAGE` | | +| `TYPE_BYTES` | New in version 2. | +| `TYPE_UINT32` | | +| `TYPE_ENUM` | | +| `TYPE_SFIXED32` | | +| `TYPE_SFIXED64` | | +| `TYPE_SINT32` | | +| `TYPE_SINT64` | | + + + + +--- +### Label + + + +| Name | Description | +| ----- | ----------- | +| `LABEL_OPTIONAL` | 0 is reserved for errors | +| `LABEL_REQUIRED` | | +| `LABEL_REPEATED` | | + + + + +--- +### OneofDescriptorProto + + +Describes a oneof. + +```yaml +"name": string +"options": .google.protobuf.OneofOptions + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `name` | `string` | | | +| `options` | [.google.protobuf.OneofOptions](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/oneof-options) | | | + + + + +--- +### EnumDescriptorProto + + +Describes an enum type. + +```yaml +"name": string +"value": []google.protobuf.EnumValueDescriptorProto +"options": .google.protobuf.EnumOptions + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `name` | `string` | | | +| `value` | [[]google.protobuf.EnumValueDescriptorProto](../descriptor.proto.sk#EnumValueDescriptorProto) | | | +| `options` | [.google.protobuf.EnumOptions](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/enum-options) | | | + + + + +--- +### EnumValueDescriptorProto + + +Describes a value within an enum. + +```yaml +"name": string +"number": int +"options": .google.protobuf.EnumValueOptions + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `name` | `string` | | | +| `number` | `int` | | | +| `options` | [.google.protobuf.EnumValueOptions](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/enum-value-options) | | | + + + + +--- +### ServiceDescriptorProto + + +Describes a service. + +```yaml +"name": string +"method": []google.protobuf.MethodDescriptorProto +"options": .google.protobuf.ServiceOptions + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `name` | `string` | | | +| `method` | [[]google.protobuf.MethodDescriptorProto](../descriptor.proto.sk#MethodDescriptorProto) | | | +| `options` | [.google.protobuf.ServiceOptions](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/service-options) | | | + + + + +--- +### MethodDescriptorProto + + +Describes a method of a service. + +```yaml +"name": string +"input_type": string +"output_type": string +"options": .google.protobuf.MethodOptions +"client_streaming": bool +"server_streaming": bool + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `name` | `string` | | | +| `input_type` | `string` | Input and output type names. These are resolved in the same way as FieldDescriptorProto.type_name, but must refer to a message type. | | +| `output_type` | `string` | | | +| `options` | [.google.protobuf.MethodOptions](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/method-options) | | | +| `client_streaming` | `bool` | Identifies if client streams multiple client messages | Default: false | +| `server_streaming` | `bool` | Identifies if server streams multiple server messages | Default: false | + + + + +--- +### FileOptions + + + +```yaml +"java_package": string +"java_outer_classname": string +"java_multiple_files": bool +"java_generate_equals_and_hash": bool +"java_string_check_utf8": bool +"optimize_for": .google.protobuf.FileOptions.OptimizeMode +"go_package": string +"cc_generic_services": bool +"java_generic_services": bool +"py_generic_services": bool +"deprecated": bool +"cc_enable_arenas": bool +"objc_class_prefix": string +"csharp_namespace": string +"swift_prefix": string +"php_class_prefix": string +"uninterpreted_option": []google.protobuf.UninterpretedOption + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `java_package` | `string` | Sets the Java package where classes generated from this .proto will be placed. By default, the proto package is used, but this is often inappropriate because proto packages do not normally start with backwards domain names. | | +| `java_outer_classname` | `string` | If set, all the classes from the .proto file are wrapped in a single outer class with the given name. This applies to both Proto1 (equivalent to the old "--one_java_file" option) and Proto2 (where a .proto always translates to a single class, but you may want to explicitly choose the class name). | | +| `java_multiple_files` | `bool` | If set true, then the Java code generator will generate a separate .java file for each top-level message, enum, and service defined in the .proto file. Thus, these types will *not* be nested inside the outer class named by java_outer_classname. However, the outer class will still be generated to contain the file's getDescriptor() method as well as any top-level extensions defined in the file. | Default: false | +| `java_generate_equals_and_hash` | `bool` | This option does nothing. | | +| `java_string_check_utf8` | `bool` | If set true, then the Java2 code generator will generate code that throws an exception whenever an attempt is made to assign a non-UTF-8 byte sequence to a string field. Message reflection will do the same. However, an extension field still accepts non-UTF-8 byte sequences. This option has no effect on when used with the lite runtime. | Default: false | +| `optimize_for` | [.google.protobuf.FileOptions.OptimizeMode](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/file-options.-optimize-mode) | | Default: SPEED | +| `go_package` | `string` | Sets the Go package where structs generated from this .proto will be placed. If omitted, the Go package will be derived from the following: - The basename of the package import path, if provided. - Otherwise, the package statement in the .proto file, if present. - Otherwise, the basename of the .proto file, without extension. | | +| `cc_generic_services` | `bool` | Should generic services be generated in each language? "Generic" services are not specific to any particular RPC system. They are generated by the main code generators in each language (without additional plugins). Generic services were the only kind of service generation supported by early versions of google.protobuf. Generic services are now considered deprecated in favor of using plugins that generate code specific to your particular RPC system. Therefore, these default to false. Old code which depends on generic services should explicitly set them to true. | Default: false | +| `java_generic_services` | `bool` | | Default: false | +| `py_generic_services` | `bool` | | Default: false | +| `deprecated` | `bool` | Is this file deprecated? Depending on the target platform, this can emit Deprecated annotations for everything in the file, or it will be completely ignored; in the very least, this is a formalization for deprecating files. | Default: false | +| `cc_enable_arenas` | `bool` | Enables the use of arenas for the proto messages in this file. This applies only to generated classes for C++. | Default: false | +| `objc_class_prefix` | `string` | Sets the objective c class prefix which is prepended to all objective c generated classes from this .proto. There is no default. | | +| `csharp_namespace` | `string` | Namespace for generated classes; defaults to the package. | | +| `swift_prefix` | `string` | By default Swift generators will take the proto package and CamelCase it replacing '.' with underscore and use that to prefix the types/symbols defined. When this options is provided, they will use this value instead to prefix the types/symbols defined. | | +| `php_class_prefix` | `string` | Sets the php class prefix which is prepended to all php generated classes from this .proto. Default is empty. | | +| `uninterpreted_option` | [[]google.protobuf.UninterpretedOption](../descriptor.proto.sk#UninterpretedOption) | The parser stores options it doesn't recognize here. See above. | | + + + + +--- +### OptimizeMode + + +Generated classes can be optimized for speed or code size. + +| Name | Description | +| ----- | ----------- | +| `SPEED` | | +| `CODE_SIZE` | etc. | +| `LITE_RUNTIME` | | + + + + +--- +### MessageOptions + + + +```yaml +"message_set_wire_format": bool +"no_standard_descriptor_accessor": bool +"deprecated": bool +"map_entry": bool +"uninterpreted_option": []google.protobuf.UninterpretedOption + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `message_set_wire_format` | `bool` | Set true to use the old proto1 MessageSet wire format for extensions. This is provided for backwards-compatibility with the MessageSet wire format. You should not use this for any other reason: It's less efficient, has fewer features, and is more complicated. The message must be defined exactly as follows: message Foo { option message_set_wire_format = true; extensions 4 to max; } Note that the message cannot have any defined fields; MessageSets only have extensions. All extensions of your type must be singular messages; e.g. they cannot be int32s, enums, or repeated messages. Because this is an option, the above two restrictions are not enforced by the protocol compiler. | Default: false | +| `no_standard_descriptor_accessor` | `bool` | Disables the generation of the standard "descriptor()" accessor, which can conflict with a field of the same name. This is meant to make migration from proto1 easier; new code should avoid fields named "descriptor". | Default: false | +| `deprecated` | `bool` | Is this message deprecated? Depending on the target platform, this can emit Deprecated annotations for the message, or it will be completely ignored; in the very least, this is a formalization for deprecating messages. | Default: false | +| `map_entry` | `bool` | Whether the message is an automatically generated map entry type for the maps field. For maps fields: map map_field = 1; The parsed descriptor looks like: message MapFieldEntry { option map_entry = true; optional KeyType key = 1; optional ValueType value = 2; } repeated MapFieldEntry map_field = 1; Implementations may choose not to generate the map_entry=true message, but use a native map in the target language to hold the keys and values. The reflection APIs in such implementions still need to work as if the field is a repeated message field. NOTE: Do not set the option in .proto files. Always use the maps syntax instead. The option should only be implicitly set by the proto compiler parser. | | +| `uninterpreted_option` | [[]google.protobuf.UninterpretedOption](../descriptor.proto.sk#UninterpretedOption) | The parser stores options it doesn't recognize here. See above. | | + + + + +--- +### FieldOptions + + + +```yaml +"ctype": .google.protobuf.FieldOptions.CType +"packed": bool +"jstype": .google.protobuf.FieldOptions.JSType +"lazy": bool +"deprecated": bool +"weak": bool +"uninterpreted_option": []google.protobuf.UninterpretedOption + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `ctype` | [.google.protobuf.FieldOptions.CType](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/field-options.c-type) | The ctype option instructs the C++ code generator to use a different representation of the field than it normally would. See the specific options below. This option is not yet implemented in the open source release -- sorry, we'll try to include it in a future version! | Default: STRING | +| `packed` | `bool` | The packed option can be enabled for repeated primitive fields to enable a more efficient representation on the wire. Rather than repeatedly writing the tag and type for each element, the entire array is encoded as a single length-delimited blob. In proto3, only explicit setting it to false will avoid using packed encoding. | | +| `jstype` | [.google.protobuf.FieldOptions.JSType](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/field-options.js-type) | The jstype option determines the JavaScript type used for values of the field. The option is permitted only for 64 bit integral and fixed types (int64, uint64, sint64, fixed64, sfixed64). By default these types are represented as JavaScript strings. This avoids loss of precision that can happen when a large value is converted to a floating point JavaScript numbers. Specifying JS_NUMBER for the jstype causes the generated JavaScript code to use the JavaScript "number" type instead of strings. This option is an enum to permit additional types to be added, e.g. goog.math.Integer. | Default: JS_NORMAL | +| `lazy` | `bool` | Should this field be parsed lazily? Lazy applies only to message-type fields. It means that when the outer message is initially parsed, the inner message's contents will not be parsed but instead stored in encoded form. The inner message will actually be parsed when it is first accessed. This is only a hint. Implementations are free to choose whether to use eager or lazy parsing regardless of the value of this option. However, setting this option true suggests that the protocol author believes that using lazy parsing on this field is worth the additional bookkeeping overhead typically needed to implement it. This option does not affect the public interface of any generated code; all method signatures remain the same. Furthermore, thread-safety of the interface is not affected by this option; const methods remain safe to call from multiple threads concurrently, while non-const methods continue to require exclusive access. Note that implementations may choose not to check required fields within a lazy sub-message. That is, calling IsInitialized() on the outer message may return true even if the inner message has missing required fields. This is necessary because otherwise the inner message would have to be parsed in order to perform the check, defeating the purpose of lazy parsing. An implementation which chooses not to check required fields must be consistent about it. That is, for any particular sub-message, the implementation must either *always* check its required fields, or *never* check its required fields, regardless of whether or not the message has been parsed. | Default: false | +| `deprecated` | `bool` | Is this field deprecated? Depending on the target platform, this can emit Deprecated annotations for accessors, or it will be completely ignored; in the very least, this is a formalization for deprecating fields. | Default: false | +| `weak` | `bool` | For Google-internal migration only. Do not use. | Default: false | +| `uninterpreted_option` | [[]google.protobuf.UninterpretedOption](../descriptor.proto.sk#UninterpretedOption) | The parser stores options it doesn't recognize here. See above. | | + + + + +--- +### CType + + + +| Name | Description | +| ----- | ----------- | +| `STRING` | Default mode. | +| `CORD` | | +| `STRING_PIECE` | | + + + + +--- +### JSType + + + +| Name | Description | +| ----- | ----------- | +| `JS_NORMAL` | Use the default type. | +| `JS_STRING` | Use JavaScript strings. | +| `JS_NUMBER` | Use JavaScript numbers. | + + + + +--- +### OneofOptions + + + +```yaml +"uninterpreted_option": []google.protobuf.UninterpretedOption + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `uninterpreted_option` | [[]google.protobuf.UninterpretedOption](../descriptor.proto.sk#UninterpretedOption) | The parser stores options it doesn't recognize here. See above. | | + + + + +--- +### EnumOptions + + + +```yaml +"allow_alias": bool +"deprecated": bool +"uninterpreted_option": []google.protobuf.UninterpretedOption + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `allow_alias` | `bool` | Set this option to true to allow mapping different tag names to the same value. | | +| `deprecated` | `bool` | Is this enum deprecated? Depending on the target platform, this can emit Deprecated annotations for the enum, or it will be completely ignored; in the very least, this is a formalization for deprecating enums. | Default: false | +| `uninterpreted_option` | [[]google.protobuf.UninterpretedOption](../descriptor.proto.sk#UninterpretedOption) | The parser stores options it doesn't recognize here. See above. | | + + + + +--- +### EnumValueOptions + + + +```yaml +"deprecated": bool +"uninterpreted_option": []google.protobuf.UninterpretedOption + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `deprecated` | `bool` | Is this enum value deprecated? Depending on the target platform, this can emit Deprecated annotations for the enum value, or it will be completely ignored; in the very least, this is a formalization for deprecating enum values. | Default: false | +| `uninterpreted_option` | [[]google.protobuf.UninterpretedOption](../descriptor.proto.sk#UninterpretedOption) | The parser stores options it doesn't recognize here. See above. | | + + + + +--- +### ServiceOptions + + + +```yaml +"deprecated": bool +"uninterpreted_option": []google.protobuf.UninterpretedOption + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `deprecated` | `bool` | Is this service deprecated? Depending on the target platform, this can emit Deprecated annotations for the service, or it will be completely ignored; in the very least, this is a formalization for deprecating services. | Default: false | +| `uninterpreted_option` | [[]google.protobuf.UninterpretedOption](../descriptor.proto.sk#UninterpretedOption) | The parser stores options it doesn't recognize here. See above. | | + + + + +--- +### MethodOptions + + + +```yaml +"deprecated": bool +"idempotency_level": .google.protobuf.MethodOptions.IdempotencyLevel +"uninterpreted_option": []google.protobuf.UninterpretedOption + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `deprecated` | `bool` | Is this method deprecated? Depending on the target platform, this can emit Deprecated annotations for the method, or it will be completely ignored; in the very least, this is a formalization for deprecating methods. | Default: false | +| `idempotency_level` | [.google.protobuf.MethodOptions.IdempotencyLevel](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/method-options.-idempotency-level) | | Default: IDEMPOTENCY_UNKNOWN | +| `uninterpreted_option` | [[]google.protobuf.UninterpretedOption](../descriptor.proto.sk#UninterpretedOption) | The parser stores options it doesn't recognize here. See above. | | + + + + +--- +### IdempotencyLevel + + +Is this method side-effect-free (or safe in HTTP parlance), or idempotent, +or neither? HTTP based RPC implementation may choose GET verb for safe +methods, and PUT verb for idempotent methods instead of the default POST. + +| Name | Description | +| ----- | ----------- | +| `IDEMPOTENCY_UNKNOWN` | | +| `NO_SIDE_EFFECTS` | | +| `IDEMPOTENT` | | + + + + +--- +### UninterpretedOption + + +A message representing a option the parser does not recognize. This only +appears in options protos created by the compiler::Parser class. +DescriptorPool resolves these when building Descriptor objects. Therefore, +options protos in descriptor objects (e.g. returned by Descriptor::options(), +or produced by Descriptor::CopyTo()) will never have UninterpretedOptions +in them. + +```yaml +"name": []google.protobuf.UninterpretedOption.NamePart +"identifier_value": string +"positive_int_value": int +"negative_int_value": int +"double_value": float +"string_value": bytes +"aggregate_value": string + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `name` | [[]google.protobuf.UninterpretedOption.NamePart](../descriptor.proto.sk#NamePart) | | | +| `identifier_value` | `string` | The value of the uninterpreted option, in whatever type the tokenizer identified it as during parsing. Exactly one of these should be set. | | +| `positive_int_value` | `int` | | | +| `negative_int_value` | `int` | | | +| `double_value` | `float` | | | +| `string_value` | `bytes` | | | +| `aggregate_value` | `string` | | | + + + + +--- +### NamePart + + +The name of the uninterpreted option. Each string represents a segment in +a dot-separated name. is_extension is true iff a segment represents an +extension (denoted with parentheses in options specs in .proto files). +E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents +"foo.(bar.baz).qux". + +```yaml +"name_part": string +"is_extension": bool + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `name_part` | `string` | | | +| `is_extension` | `bool` | | | + + + + +--- +### SourceCodeInfo + + +Encapsulates information about the original source file from which a +FileDescriptorProto was generated. + +```yaml +"location": []google.protobuf.SourceCodeInfo.Location + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `location` | [[]google.protobuf.SourceCodeInfo.Location](../descriptor.proto.sk#Location) | A Location identifies a piece of source code in a .proto file which corresponds to a particular definition. This information is intended to be useful to IDEs, code indexers, documentation generators, and similar tools. For example, say we have a file like: message Foo { optional string foo = 1; } Let's look at just the field definition: optional string foo = 1; ^ ^^ ^^ ^ ^^^ a bc de f ghi We have the following locations: span path represents [a,i) [ 4, 0, 2, 0 ] The whole field definition. [a,b) [ 4, 0, 2, 0, 4 ] The label (optional). [c,d) [ 4, 0, 2, 0, 5 ] The type (string). [e,f) [ 4, 0, 2, 0, 1 ] The name (foo). [g,h) [ 4, 0, 2, 0, 3 ] The number (1). Notes: - A location may refer to a repeated field itself (i.e. not to any particular index within it). This is used whenever a set of elements are logically enclosed in a single code segment. For example, an entire extend block (possibly containing multiple extension definitions) will have an outer location whose path refers to the "extensions" repeated field without an index. - Multiple locations may have the same path. This happens when a single logical declaration is spread out across multiple places. The most obvious example is the "extend" block again -- there may be multiple extend blocks in the same scope, each of which will have the same path. - A location's span is not always a subset of its parent's span. For example, the "extendee" of an extension declaration appears at the beginning of the "extend" block and is shared by all extensions within the block. - Just because a location's span is a subset of some other location's span does not mean that it is a descendent. For example, a "group" defines both a type and a field in a single declaration. Thus, the locations corresponding to the type and field and their components will overlap. - Code which tries to interpret locations should probably be designed to ignore those that it doesn't understand, as more types of locations could be recorded in the future. | | + + + + +--- +### Location + + + +```yaml +"path": []int +"span": []int +"leading_comments": string +"trailing_comments": string +"leading_detached_comments": []string + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `path` | `[]int` | Identifies which part of the FileDescriptorProto was defined at this location. Each element is a field number or an index. They form a path from the root FileDescriptorProto to the place where the definition. For example, this path: [ 4, 3, 2, 7, 1 ] refers to: file.message_type(3) // 4, 3 .field(7) // 2, 7 .name() // 1 This is because FileDescriptorProto.message_type has field number 4: repeated DescriptorProto message_type = 4; and DescriptorProto.field has field number 2: repeated FieldDescriptorProto field = 2; and FieldDescriptorProto.name has field number 1: optional string name = 1; Thus, the above path gives the location of a field name. If we removed the last element: [ 4, 3, 2, 7 ] this path refers to the whole field declaration (from the beginning of the label to the terminating semicolon). | | +| `span` | `[]int` | Always has exactly three or four elements: start line, start column, end line (optional, otherwise assumed same as start line), end column. These are packed into a single field for efficiency. Note that line and column numbers are zero-based -- typically you will want to add 1 to each before displaying to a user. | | +| `leading_comments` | `string` | If this SourceCodeInfo represents a complete declaration, these are any comments appearing before and after the declaration which appear to be attached to the declaration. A series of line comments appearing on consecutive lines, with no other tokens appearing on those lines, will be treated as a single comment. leading_detached_comments will keep paragraphs of comments that appear before (but not connected to) the current element. Each paragraph, separated by empty lines, will be one comment element in the repeated field. Only the comment content is provided; comment markers (e.g. //) are stripped out. For block comments, leading whitespace and an asterisk will be stripped from the beginning of each line other than the first. Newlines are included in the output. Examples: optional int32 foo = 1; // Comment attached to foo. // Comment attached to bar. optional int32 bar = 2; optional string baz = 3; // Comment attached to baz. // Another line attached to baz. // Comment attached to qux. // // Another line attached to qux. optional double qux = 4; // Detached comment for corge. This is not leading or trailing comments // to qux or corge because there are blank lines separating it from // both. // Detached comment for corge paragraph 2. optional string corge = 5; /* Block comment attached * to corge. Leading asterisks * will be removed. */ /* Block comment attached to * grault. */ optional int32 grault = 6; // ignored detached comments. | | +| `trailing_comments` | `string` | | | +| `leading_detached_comments` | `[]string` | | | + + + + +--- +### GeneratedCodeInfo + + +Describes the relationship between generated code and its original source +file. A GeneratedCodeInfo message is associated with only one generated +source file, but may contain references to different source .proto files. + +```yaml +"annotation": []google.protobuf.GeneratedCodeInfo.Annotation + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `annotation` | [[]google.protobuf.GeneratedCodeInfo.Annotation](../descriptor.proto.sk#Annotation) | An Annotation connects some span of text in generated code to an element of its generating .proto file. | | + + + + +--- +### Annotation + + + +```yaml +"path": []int +"source_file": string +"begin": int +"end": int + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `path` | `[]int` | Identifies the element in the original source .proto file. This field is formatted the same as SourceCodeInfo.Location.path. | | +| `source_file` | `string` | Identifies the filesystem path to the original source .proto. | | +| `begin` | `int` | Identifies the starting offset in bytes in the generated code that relates to the identified object. | | +| `end` | `int` | Identifies the ending offset in bytes in the generated code that relates to the identified offset. The end offset should be one past the last relevant byte (so the length of the text = end - begin). | | + + + + + + + + diff --git a/docs/v1/google/protobuf/duration.proto.sk.md b/docs/v1/google/protobuf/duration.proto.sk.md new file mode 100644 index 0000000..b543ec5 --- /dev/null +++ b/docs/v1/google/protobuf/duration.proto.sk.md @@ -0,0 +1,137 @@ + +--- +title: "duration.proto" +weight: 5 +--- + + + + +### Package: `google.protobuf` +Protocol Buffers - Google's data interchange format +Copyright 2008 Google Inc. All rights reserved. +https://developers.google.com/protocol-buffers/ + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +##### Types: + + +- [Duration](#Duration) + + + + +##### Source File: `google/protobuf/duration.proto` + + + + + +--- +### Duration + + +A Duration represents a signed, fixed-length span of time represented +as a count of seconds and fractions of seconds at nanosecond +resolution. It is independent of any calendar and concepts like "day" +or "month". It is related to Timestamp in that the difference between +two Timestamp values is a Duration and it can be added or subtracted +from a Timestamp. Range is approximately +-10,000 years. + +# Examples + +Example 1: Compute Duration from two Timestamps in pseudo code. + + Timestamp start = ...; + Timestamp end = ...; + Duration duration = ...; + + duration.seconds = end.seconds - start.seconds; + duration.nanos = end.nanos - start.nanos; + + if (duration.seconds < 0 && duration.nanos > 0) { + duration.seconds += 1; + duration.nanos -= 1000000000; + } else if (durations.seconds > 0 && duration.nanos < 0) { + duration.seconds -= 1; + duration.nanos += 1000000000; + } + +Example 2: Compute Timestamp from Timestamp + Duration in pseudo code. + + Timestamp start = ...; + Duration duration = ...; + Timestamp end = ...; + + end.seconds = start.seconds + duration.seconds; + end.nanos = start.nanos + duration.nanos; + + if (end.nanos < 0) { + end.seconds -= 1; + end.nanos += 1000000000; + } else if (end.nanos >= 1000000000) { + end.seconds += 1; + end.nanos -= 1000000000; + } + +Example 3: Compute Duration from datetime.timedelta in Python. + + td = datetime.timedelta(days=3, minutes=10) + duration = Duration() + duration.FromTimedelta(td) + +# JSON Mapping + +In JSON format, the Duration type is encoded as a string rather than an +object, where the string ends in the suffix "s" (indicating seconds) and +is preceded by the number of seconds, with nanoseconds expressed as +fractional seconds. For example, 3 seconds with 0 nanoseconds should be +encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should +be expressed in JSON format as "3.000000001s", and 3 seconds and 1 +microsecond should be expressed in JSON format as "3.000001s". + +```yaml +"seconds": int +"nanos": int + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `seconds` | `int` | Signed seconds of the span of time. Must be from -315,576,000,000 to +315,576,000,000 inclusive. Note: these bounds are computed from: 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years | | +| `nanos` | `int` | Signed fractions of a second at nanosecond resolution of the span of time. Durations less than one second are represented with a 0 `seconds` field and a positive or negative `nanos` field. For durations of one second or more, a non-zero value for the `nanos` field must be of the same sign as the `seconds` field. Must be from -999,999,999 to +999,999,999 inclusive. | | + + + + + + + + diff --git a/docs/v1/google/protobuf/struct.proto.sk.md b/docs/v1/google/protobuf/struct.proto.sk.md new file mode 100644 index 0000000..8e0a4e8 --- /dev/null +++ b/docs/v1/google/protobuf/struct.proto.sk.md @@ -0,0 +1,158 @@ + +--- +title: "struct.proto" +weight: 5 +--- + + + + +### Package: `google.protobuf` +Protocol Buffers - Google's data interchange format +Copyright 2008 Google Inc. All rights reserved. +https://developers.google.com/protocol-buffers/ + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +##### Types: + + +- [Struct](#Struct) +- [Value](#Value) +- [ListValue](#ListValue) + + + + +##### Enums: + + + - [NullValue](#NullValue) + + + +##### Source File: `google/protobuf/struct.proto` + + + + + +--- +### Struct + + +`Struct` represents a structured data value, consisting of fields +which map to dynamically typed values. In some languages, `Struct` +might be supported by a native representation. For example, in +scripting languages like JS a struct is represented as an +object. The details of that representation are described together +with the proto support for the language. + +The JSON representation for `Struct` is JSON object. + +```yaml +"fields": map + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `fields` | `map` | Unordered map of dynamically typed values. | | + + + + +--- +### Value + + +`Value` represents a dynamically typed value which can be either +null, a number, a string, a boolean, a recursive struct value, or a +list of values. A producer of value is expected to set one of that +variants, absence of any variant indicates an error. + +The JSON representation for `Value` is JSON value. + +```yaml +"null_value": .google.protobuf.NullValue +"number_value": float +"string_value": string +"bool_value": bool +"struct_value": .google.protobuf.Struct +"list_value": .google.protobuf.ListValue + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `null_value` | [.google.protobuf.NullValue](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/null-value) | Represents a null value. | | +| `number_value` | `float` | Represents a double value. | | +| `string_value` | `string` | Represents a string value. | | +| `bool_value` | `bool` | Represents a boolean value. | | +| `struct_value` | [.google.protobuf.Struct](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/struct) | Represents a structured value. | | +| `list_value` | [.google.protobuf.ListValue](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/list-value) | Represents a repeated `Value`. | | + + + + +--- +### ListValue + + +`ListValue` is a wrapper around a repeated field of values. + +The JSON representation for `ListValue` is JSON array. + +```yaml +"values": []google.protobuf.Value + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `values` | [[]google.protobuf.Value](../struct.proto.sk#Value) | Repeated field of dynamically typed values. | | + + + + +### NullValue + +Description: `NullValue` is a singleton enumeration to represent the null value for the +`Value` type union. + + The JSON representation for `NullValue` is JSON `null`. + +| Name | Description | +| ----- | ----------- | +| NULL_VALUE | Null value. | + + + + + diff --git a/docs/v1/google/protobuf/timestamp.proto.sk.md b/docs/v1/google/protobuf/timestamp.proto.sk.md new file mode 100644 index 0000000..d1a72fc --- /dev/null +++ b/docs/v1/google/protobuf/timestamp.proto.sk.md @@ -0,0 +1,155 @@ + +--- +title: "timestamp.proto" +weight: 5 +--- + + + + +### Package: `google.protobuf` +Protocol Buffers - Google's data interchange format +Copyright 2008 Google Inc. All rights reserved. +https://developers.google.com/protocol-buffers/ + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +##### Types: + + +- [Timestamp](#Timestamp) + + + + +##### Source File: `google/protobuf/timestamp.proto` + + + + + +--- +### Timestamp + + +A Timestamp represents a point in time independent of any time zone +or calendar, represented as seconds and fractions of seconds at +nanosecond resolution in UTC Epoch time. It is encoded using the +Proleptic Gregorian Calendar which extends the Gregorian calendar +backwards to year one. It is encoded assuming all minutes are 60 +seconds long, i.e. leap seconds are "smeared" so that no leap second +table is needed for interpretation. Range is from +0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. +By restricting to that range, we ensure that we can convert to +and from RFC 3339 date strings. +See [https://www.ietf.org/rfc/rfc3339.txt](https://www.ietf.org/rfc/rfc3339.txt). + +# Examples + +Example 1: Compute Timestamp from POSIX `time()`. + + Timestamp timestamp; + timestamp.set_seconds(time(NULL)); + timestamp.set_nanos(0); + +Example 2: Compute Timestamp from POSIX `gettimeofday()`. + + struct timeval tv; + gettimeofday(&tv, NULL); + + Timestamp timestamp; + timestamp.set_seconds(tv.tv_sec); + timestamp.set_nanos(tv.tv_usec * 1000); + +Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. + + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; + + // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z + // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. + Timestamp timestamp; + timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); + timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); + +Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. + + long millis = System.currentTimeMillis(); + + Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) + .setNanos((int) ((millis % 1000) * 1000000)).build(); + + +Example 5: Compute Timestamp from current time in Python. + + timestamp = Timestamp() + timestamp.GetCurrentTime() + +# JSON Mapping + +In JSON format, the Timestamp type is encoded as a string in the +[RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the +format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" +where {year} is always expressed using four digits while {month}, {day}, +{hour}, {min}, and {sec} are zero-padded to two digits each. The fractional +seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), +are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone +is required, though only UTC (as indicated by "Z") is presently supported. + +For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past +01:30 UTC on January 15, 2017. + +In JavaScript, one can convert a Date object to this format using the +standard [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString] +method. In Python, a standard `datetime.datetime` object can be converted +to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) +with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one +can use the Joda Time's [`ISODateTimeFormat.dateTime()`]( +http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()) +to obtain a formatter capable of generating timestamps in this format. + +```yaml +"seconds": int +"nanos": int + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `seconds` | `int` | Represents seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive. | | +| `nanos` | `int` | Non-negative fractions of a second at nanosecond resolution. Negative second values with fractions must still have non-negative nanos values that count forward in time. Must be from 0 to 999,999,999 inclusive. | | + + + + + + + + diff --git a/docs/v1/google/protobuf/wrappers.proto.sk.md b/docs/v1/google/protobuf/wrappers.proto.sk.md new file mode 100644 index 0000000..c43edc4 --- /dev/null +++ b/docs/v1/google/protobuf/wrappers.proto.sk.md @@ -0,0 +1,252 @@ + +--- +title: "wrappers.proto" +weight: 5 +--- + + + + +### Package: `google.protobuf` +Protocol Buffers - Google's data interchange format +Copyright 2008 Google Inc. All rights reserved. +https://developers.google.com/protocol-buffers/ + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +Wrappers for primitive (non-message) types. These types are useful +for embedding primitives in the `google.protobuf.Any` type and for places +where we need to distinguish between the absence of a primitive +typed field and its default value. + + + +##### Types: + + +- [DoubleValue](#DoubleValue) +- [FloatValue](#FloatValue) +- [Int64Value](#Int64Value) +- [UInt64Value](#UInt64Value) +- [Int32Value](#Int32Value) +- [UInt32Value](#UInt32Value) +- [BoolValue](#BoolValue) +- [StringValue](#StringValue) +- [BytesValue](#BytesValue) + + + + +##### Source File: `google/protobuf/wrappers.proto` + + + + + +--- +### DoubleValue + + +Wrapper message for `double`. + +The JSON representation for `DoubleValue` is JSON number. + +```yaml +"value": float + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `value` | `float` | The double value. | | + + + + +--- +### FloatValue + + +Wrapper message for `float`. + +The JSON representation for `FloatValue` is JSON number. + +```yaml +"value": float + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `value` | `float` | The float value. | | + + + + +--- +### Int64Value + + +Wrapper message for `int64`. + +The JSON representation for `Int64Value` is JSON string. + +```yaml +"value": int + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `value` | `int` | The int64 value. | | + + + + +--- +### UInt64Value + + +Wrapper message for `uint64`. + +The JSON representation for `UInt64Value` is JSON string. + +```yaml +"value": int + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `value` | `int` | The uint64 value. | | + + + + +--- +### Int32Value + + +Wrapper message for `int32`. + +The JSON representation for `Int32Value` is JSON number. + +```yaml +"value": int + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `value` | `int` | The int32 value. | | + + + + +--- +### UInt32Value + + +Wrapper message for `uint32`. + +The JSON representation for `UInt32Value` is JSON number. + +```yaml +"value": int + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `value` | `int` | The uint32 value. | | + + + + +--- +### BoolValue + + +Wrapper message for `bool`. + +The JSON representation for `BoolValue` is JSON `true` and `false`. + +```yaml +"value": bool + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `value` | `bool` | The bool value. | | + + + + +--- +### StringValue + + +Wrapper message for `string`. + +The JSON representation for `StringValue` is JSON string. + +```yaml +"value": string + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `value` | `string` | The string value. | | + + + + +--- +### BytesValue + + +Wrapper message for `bytes`. + +The JSON representation for `BytesValue` is JSON string. + +```yaml +"value": bytes + +``` + +| Field | Type | Description | Default | +| ----- | ---- | ----------- |----------- | +| `value` | `bytes` | The bytes value. | | + + + + + + + + diff --git a/docs/v1/resolver_map.md b/docs/v1/resolver_map.md deleted file mode 100644 index d0efc1b..0000000 --- a/docs/v1/resolver_map.md +++ /dev/null @@ -1,225 +0,0 @@ - - -## Contents - - [ResolverMap](#sqoop.api.v1.ResolverMap) - - [TypeResolver](#sqoop.api.v1.TypeResolver) - - [Resolver](#sqoop.api.v1.Resolver) - - [GlooResolver](#sqoop.api.v1.GlooResolver) - - [Function](#sqoop.api.v1.Function) - - [MultiFunction](#sqoop.api.v1.MultiFunction) - - [WeightedFunction](#sqoop.api.v1.WeightedFunction) - - [TemplateResolver](#sqoop.api.v1.TemplateResolver) - - [NodeJSResolver](#sqoop.api.v1.NodeJSResolver) - - - - -

    Top

    - - - - - - -### ResolverMap -The ResolverMap object maps Resolvers to the fields in the GraphQL Schema -The skeleton of a Resolver Map will be generated by Sqoop automatically when a schema -is read or updated if one does not alreay exist. - - -```yaml -name: string -types: map -status: {gloo.api.v1.Status} -metadata: {gloo.api.v1.Metadata} - -``` -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| name | string | | Name of the Resolver Map. Resolver Map names must be unique - -Resolver Map Names must be unique and follow the following syntax rules: One or more lowercase rfc1035/rfc1123 labels separated by '.' with a maximum length of 253 characters. | -| types | map<string,TypeResolver> | | Types is a map of Type Names (defined in the schema) to a TypeResolver, which contain resolvers for the specific fields of the type | -| status | [gloo.api.v1.Status](schema.md#gloo.api.v1.Status) | | Status indicates the validation status of the role resource. Status is read-only by clients, and set by gloo during validation | -| metadata | [gloo.api.v1.Metadata](schema.md#gloo.api.v1.Metadata) | | Metadata contains the resource metadata for the role | - - - - - - - - -### TypeResolver -TypeResolver contains the individual resolvers for each field for a specific type - - -```yaml -fields: map - -``` -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| fields | map<string,Resolver> | | This is a map of Field Names to the resolver that Sqoop should invoke when a query arrives for that field | - - - - - - - - -### Resolver -Resolvers define the actual logic Sqoop needs to know in order to resolve a specific field query - - -```yaml -gloo_resolver: {GlooResolver} -template_resolver: {TemplateResolver} -nodejs_resolver: {NodeJSResolver} - -``` -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| gloo_resolver | [GlooResolver](resolver_map.md#sqoop.api.v1.GlooResolver) | | a GlooResolver, which leverages Gloo to retrieve data from backend services and functions for the query | -| template_resolver | [TemplateResolver](resolver_map.md#sqoop.api.v1.TemplateResolver) | | a TemplateResolver, which uses Go Templates to generate data for the query | -| nodejs_resolver | [NodeJSResolver](resolver_map.md#sqoop.api.v1.NodeJSResolver) | | a NodeJSResolver, which calls NodeJS functions to return data for the query | - - - - - - - - -### GlooResolver -GlooResolvers are the "meat" of Sqoop. GlooResolvers tell Sqoop how to invoke a "Gloo Function" - - -```yaml -request_template: string -response_template: string -content_type: string -single_function: {Function} -multi_function: {MultiFunction} - -``` -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| request_template | string | | the Request Template, if specified, will become the body of the HTTP request used to invoke a function through Gloo input parameters, if needed, should be specified in the request template. See Sqoop's [Resolver documentation](TODO) for more information on Request Templates. | -| response_template | string | | The response template, if specified, will transform the body of HTTP responses returned by Gloo functions. This field should be used if the object returned by the Gloo Function does not match the type specified in the GraphQL schema. It can also be used to modify or transform responses from their original state. See Sqoop's [Resolver documentation](TODO) for more information on Response Templates. | -| content_type | string | | Optional. Use to set the outbound HTTP Request header `Content-Type`. Defaults to `application/json` | -| single_function | [Function](resolver_map.md#sqoop.api.v1.Function) | | SingleFunction specifies this resolver will always invoke a single function. | -| multi_function | [MultiFunction](resolver_map.md#sqoop.api.v1.MultiFunction) | | MultiFunction specifies the resolver will distribute invocation across multiple functions | - - - - - - - - -### Function -A reference to a function known to Gloo - - -```yaml -upstream: string -function: string - -``` -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| upstream | string | | Name of the Gloo Upstream that provides this function | -| function | string | | Name of the function itself. See Gloo documentation for more details on functions in Gloo | - - - - - - - - -### MultiFunction -A reference to a list of functions known to Gloo - - -```yaml -weighted_functions: [{WeightedFunction}] - -``` -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| weighted_functions | [WeightedFunction](resolver_map.md#sqoop.api.v1.WeightedFunction) | repeated | A list of functions with weights. Must have size >= 1 | - - - - - - - - -### WeightedFunction - - - -```yaml -function: {Function} -weight: uint32 - -``` -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| function | [Function](resolver_map.md#sqoop.api.v1.Function) | | the function to call | -| weight | uint32 | | Invoking each functoion will be balanced by the ratio of the function's weight to the total weight on a resolver | - - - - - - - - -### TemplateResolver -A Go-template which will return data for a Resolver without making a function call. Template Resolvers can make use -of Sqoop's builtin template functions as well as the data provided by the Params object to the resolver. -Read more about Templates and Resolvers in Sqoop's [Resolver documentation](TODO). - - -```yaml -inline_template: string - -``` -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| inline_template | string | | the Go template as an inline string | - - - - - - - - -### NodeJSResolver -NOTE: currently unsupported - - -```yaml -inline_code: string - -``` -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| inline_code | string | | | - - - - - - - - - - - diff --git a/docs/v1/schema.md b/docs/v1/schema.md deleted file mode 100644 index dbb8696..0000000 --- a/docs/v1/schema.md +++ /dev/null @@ -1,46 +0,0 @@ - - -## Contents - - [Schema](#sqoop.api.v1.Schema) - - - - -

    Top

    - - - - - - -### Schema -The Schema object wraps the user's GraphQL Schema, which is stored as an inline string. -The Schema Object contains a Status field which is used by Sqoop to validate the user's input schema. - - -```yaml -name: string -resolver_map: string -inline_schema: string -status: {gloo.api.v1.Status} -metadata: {gloo.api.v1.Metadata} - -``` -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| name | string | | Schema Names must be unique and follow the following syntax rules: One or more lowercase rfc1035/rfc1123 labels separated by '.' with a maximum length of 253 characters. | -| resolver_map | string | | name of the resolver map to use to resolve this schema. if the user leaves this empty, Sqoop will generate the skeleton of a resolver map for the user | -| inline_schema | string | | inline the entire graphql schema as a string here | -| status | [gloo.api.v1.Status](schema.md#gloo.api.v1.Status) | | Status indicates the validation status of the role resource. Status is read-only by clients, and set by gloo during validation | -| metadata | [gloo.api.v1.Metadata](schema.md#gloo.api.v1.Metadata) | | Metadata contains the resource metadata for the role | - - - - - - - - - - - diff --git a/docs/v1/sqoop.solo.io.project.sk.md b/docs/v1/sqoop.solo.io.project.sk.md new file mode 100644 index 0000000..f332f2d --- /dev/null +++ b/docs/v1/sqoop.solo.io.project.sk.md @@ -0,0 +1,24 @@ + +--- +title: "sqoop.solo.io.project" +weight: 5 +--- + + + + + +### API Reference for + +API Version: `sqoop.solo.io.v1` + + + +### API Resources: +- [Proxy](../github.com/solo-io/gloo/projects/gloo/api/v1/proxy.proto.sk#Proxy) +- [ResolverMap](../github.com/solo-io/sqoop/api/v1/resolver_map.proto.sk#ResolverMap) +- [Schema](../github.com/solo-io/sqoop/api/v1/schema.proto.sk#Schema) + + + + diff --git a/examples/petstore/README.md b/examples/petstore/README.md index 6f8d1e8..30e83a8 100644 --- a/examples/petstore/README.md +++ b/examples/petstore/README.md @@ -6,7 +6,7 @@ - [`glooctl`](https://github.com/solo-io/gloo): (OPTIONAL) to see how Sqoop is interacting with the underlying system - Kubernetes v1.8+ deployed somewhere. [Minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/) is a great way to get a cluster up quickly. - +This tutorial will install sqoop into the namespace `gloo-system` by default, this is configurable from the `sqoopctl` cli. ### Steps @@ -17,8 +17,7 @@ #### Deploy the Pet Store - kubectl apply \ - -f https://raw.githubusercontent.com/solo-io/gloo/master/example/petstore/petstore.yaml + kubectl apply -n gloo-system -f petstore.yaml #### OPTIONAL: View the petstore functions using `glooctl`: @@ -27,22 +26,100 @@ +--------------------------------+------------+----------+-------------+ | NAME | TYPE | STATUS | FUNCTION | +--------------------------------+------------+----------+-------------+ - | default-petstore-8080 | kubernetes | Accepted | addPet | + | gloo-system-petstore-8080 | kubernetes | Accepted | addPet | | | | | deletePet | | | | | findPetById | | | | | findPets | - | gloo-system-control-plane-8081 | kubernetes | Accepted | | - | gloo-system-ingress-8080 | kubernetes | Accepted | | - | gloo-system-ingress-8443 | kubernetes | Accepted | | +--------------------------------+------------+----------+-------------+ -The upstream we want to see is `default-petstore-8080`. The functions `addPet`, `deletePet`, `findPetById`, and `findPets` +The upstream we want to see is `gloo-system-petstore-8080`. The functions `addPet`, `deletePet`, `findPetById`, and `findPets` will become the resolvers for our GraphQL schema. +##### Alternatively: find the upstreams using `kubectl` +```bash +kubectl get upstreams -n gloo-system + +NAME AGE +gloo-system-gloo-9977 1h +gloo-system-petstore-8080 1h +gloo-system-sqoop-9090 1h +``` + +The upstream we are interested in is the petstore, so we run the following to find the functions: + +```bash +kubectl get upstreams -n gloo-system gloo-system-petstore-8080 -oyaml + +apiVersion: gloo.solo.io/v1 +kind: Upstream +metadata: + labels: + discovered_by: kubernetesplugin + service: petstore + name: gloo-system-petstore-8080 + namespace: gloo-system +spec: + upstreamSpec: + kube: + selector: + app: petstore + serviceName: petstore + serviceNamespace: gloo-system + servicePort: 8080 + serviceSpec: + rest: + swaggerInfo: + urlå: http://petstore.gloo-system.svc.cluster.local:8080/swagger.json + transformations: + addPet: + body: + text: '{"id": {{ default(id, "") }},"name": "{{ default(name, "")}}","tag": + "{{ default(tag, "")}}"}' + headers: + :method: + text: POST + :path: + text: /api/pets + content-type: + text: application/json + deletePet: + headers: + :method: + text: DELETE + :path: + text: /api/pets/{{ default(id, "") }} + content-type: + text: application/json + findPetById: + body: {} + headers: + :method: + text: GET + :path: + text: /api/pets/{{ default(id, "") }} + content-length: + text: "0" + content-type: {} + transfer-encoding: {} + findPets: + body: {} + headers: + :method: + text: GET + :path: + text: /api/pets?tags={{default(tags, "")}}&limit={{default(limit, + "")}} + content-length: + text: "0" + content-type: {} + transfer-encoding: {} + +``` + #### Create a GraphQL Schema -Copy and paste the following schema into `petstore.graphql` (or wherever you like): +An example schema is located in `petstore.schema.graphql` ```graphql # The query type, represents all of the entry points into our object graph @@ -58,7 +135,6 @@ type Mutation { type Pet{ id: ID! name: String! - status: Status! } input InputPet{ @@ -66,11 +142,6 @@ input InputPet{ name: String! tag: String } - -enum Status { - pending - available -} ``` #### Upload the Schema @@ -106,7 +177,6 @@ types: fields: id: {} name: {} - status: {} Query: fields: pet: {} @@ -124,12 +194,12 @@ Let's use `sqoopctl` to register some resolvers. ```bash # register findPetById for Query.pets (specifying no arguments) -sqoopctl resolvermap register -u default-petstore-8080 -f findPetById Query pets +sqoopctl resolvermap register -u gloo-system-petstore-8080 -f findPetById Query pets # register a resolver for Query.pet -sqoopctl resolvermap register -u default-petstore-8080 -f findPetById Query pet +sqoopctl resolvermap register -u gloo-system-petstore-8080 -f findPetById Query pet # register a resolver for Mutation.addPet # the request template tells Sqoop to use the Variable "pet" as an argument -sqoopctl resolvermap register -u default-petstore-8080 -f addPet Mutation addPet --request-template '{{ marshal (index .Args "pet") }}' +sqoopctl resolvermap register -u gloo-system-petstore-8080 -f addPet Mutation addPet --request-template '{{ marshal (index .Args "pet") }}' ``` That's it! Now we should have a functioning GraphQL frontend for our REST service. diff --git a/examples/petstore/demo.sh b/examples/petstore/demo.sh deleted file mode 100644 index 229c4a7..0000000 --- a/examples/petstore/demo.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -alias sqoopctl=${PWD}/_output/sqoopctl - -echo Installing Sqoop -k apply -f ${GOPATH}/src/github.com/solo-io/sqoop/install/kube/install.yaml - -echo Deploying Petstore -k apply -f ${GOPATH}/src/github.com/solo-io/gloo/example/petstore/petstore.yaml - -sqoopctl schema create -f examples/petstore/petstore.schema.graphql petstore - -sqoopctl resolvermap register -u default-petstore-8080 -f findPetById Query pets -sqoopctl resolvermap register -u default-petstore-8080 -f findPetById Query pet --request-template '{{ marshal .Args }}' -sqoopctl resolvermap register -u default-petstore-8080 -f addPet Mutation addPet --request-template '{{ marshal (index .Args "pet") }}' diff --git a/examples/petstore/petstore.schema.graphql b/examples/petstore/petstore.schema.graphql index 0d5c733..902b6c3 100644 --- a/examples/petstore/petstore.schema.graphql +++ b/examples/petstore/petstore.schema.graphql @@ -11,16 +11,10 @@ type Mutation { type Pet{ id: ID! name: String! - status: Status! } input InputPet{ id: ID! name: String! tag: String -} - -enum Status { - pending - available -} +} \ No newline at end of file diff --git a/examples/petstore/petstore.yaml b/examples/petstore/petstore.yaml new file mode 100644 index 0000000..d529ecc --- /dev/null +++ b/examples/petstore/petstore.yaml @@ -0,0 +1,41 @@ +--- +# Source: petstore/templates/petstore.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: petstore + name: petstore + namespace: gloo-system +spec: + selector: + matchLabels: + app: petstore + replicas: 1 + template: + metadata: + labels: + app: petstore + spec: + containers: + - image: soloio/petstore-example:latest + name: petstore + ports: + - containerPort: 8080 + protocol: TCP + name: http +--- +apiVersion: v1 +kind: Service +metadata: + name: petstore + namespace: gloo-system + labels: + service: petstore +spec: + ports: + - name: http + port: 8080 + protocol: TCP + selector: + app: petstore \ No newline at end of file diff --git a/examples/starwars/Dockerfile b/examples/starwars/Dockerfile deleted file mode 100644 index 65eb964..0000000 --- a/examples/starwars/Dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -FROM alpine:3.7 -COPY starwars /starwars -EXPOSE 1234 -ENTRYPOINT ["/starwars"] diff --git a/examples/starwars/Makefile b/examples/starwars/Makefile deleted file mode 100644 index 880edd2..0000000 --- a/examples/starwars/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -SOURCES := $(shell find . -name "*.go") - -BINARY:=starwars -IMAGE_TAG?=latest -build: $(BINARY) -OUTPUT_DIR ?= . - -$(BINARY): $(SOURCES) - CGO_ENABLED=0 GOOS=linux go build -v -i -o $(OUTPUT_DIR)/$@ *.go - -docker: $(BINARY) - docker build -t soloio/$(BINARY)-example:$(IMAGE_TAG) . - -push: docker - docker push soloio/$(BINARY)-example:$(IMAGE_TAG) - -clean: - rm -f $(BINARY) diff --git a/examples/starwars/envoy-config.yaml b/examples/starwars/envoy-config.yaml deleted file mode 100644 index 51f2eb8..0000000 --- a/examples/starwars/envoy-config.yaml +++ /dev/null @@ -1,28 +0,0 @@ -node: - cluster: sqoop - id: sqoop~1 -static_resources: - clusters: - - name: xds_cluster - connect_timeout: 5.000s - hosts: - - socket_address: - address: 127.0.0.1 - port_value: 8081 - http2_protocol_options: {} - type: STATIC -dynamic_resources: - ads_config: - api_type: GRPC - grpc_services: - - envoy_grpc: {cluster_name: xds_cluster} - cds_config: - ads: {} - lds_config: - ads: {} -admin: - access_log_path: /dev/null - address: - socket_address: - address: 0.0.0.0 - port_value: 19000 diff --git a/examples/starwars/server/server.go b/examples/starwars/server/server.go deleted file mode 100644 index 902a8e0..0000000 --- a/examples/starwars/server/server.go +++ /dev/null @@ -1,83 +0,0 @@ -package server - -import ( - "encoding/json" - "net/http" - - "github.com/gorilla/mux" - "github.com/solo-io/gloo/pkg/log" - "github.com/solo-io/sqoop/examples/starwars/imported/starwars" -) - -var baseResolvers = starwars.NewResolver() - -func loggingMiddleware(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Log request path - log.Printf("%v", r.RequestURI) - // Call the next handler, which can be another middleware in the chain, or the final handler. - next.ServeHTTP(w, r) - }) -} - -// our main function -func New() *mux.Router { - router := mux.NewRouter() - router.HandleFunc("/api/hero", GetHero).Methods("GET") - router.HandleFunc("/api/character", GetCharacter).Methods("GET") - // needs to be POST because there's a body - router.HandleFunc("/api/characters", GetCharacters).Methods("POST") - - router.Use(loggingMiddleware) - - return router -} - -func GetHero(w http.ResponseWriter, r *http.Request) { - hero, err := baseResolvers.Query_hero(r.Context(), "") - if err != nil { - log.Printf("request failed: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if err := json.NewEncoder(w).Encode(hero); err != nil { - log.Printf("encoding failed: %v", err) - } -} - -func GetCharacter(w http.ResponseWriter, r *http.Request) { - id := r.Header.Get("x-id") - friends, err := baseResolvers.Query_character(r.Context(), id) - if err != nil { - log.Printf("request failed: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - log.Printf("%v", friends) - if err := json.NewEncoder(w).Encode(friends); err != nil { - log.Printf("encoding failed: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - } -} - -func GetCharacters(w http.ResponseWriter, r *http.Request) { - var input []string - if err := json.NewDecoder(r.Body).Decode(&input); err != nil { - log.Printf("decoding json: %v", err) - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - var characters []starwars.Character - for _, id := range input { - char, err := baseResolvers.Query_character(r.Context(), id) - if err != nil { - log.Printf("request failed: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - characters = append(characters, char) - } - if err := json.NewEncoder(w).Encode(characters); err != nil { - log.Printf("encoding failed: %v", err) - } -} diff --git a/examples/starwars/sqoop-config/resolver_maps/starwars_resolvers.yaml b/examples/starwars/sqoop-config/resolver_maps/starwars_resolvers.yaml deleted file mode 100644 index 3f4e94d..0000000 --- a/examples/starwars/sqoop-config/resolver_maps/starwars_resolvers.yaml +++ /dev/null @@ -1,43 +0,0 @@ -name: starwars-resolvers -types: - Droid: - fields: - appearsIn: - tempalte_resolver: - inline_template: '{{ index .Parent "appears_in" }}}' - friends: - gloo_resolver: - request_template: '{{ marshal (index .Parent "friend_ids") }}' - single_function: - function: GetCharacters - upstream: starwars-rest - Human: - fields: - appearsIn: - tempalte_resolver: - inline_template: '{{ index .Parent "appears_in" }}}' - friends: - gloo_resolver: - request_template: '{{ marshal (index .Parent "friend_ids") }}' - single_function: - function: GetCharacters - upstream: starwars-rest - Query: - fields: - droid: - gloo_resolver: - request_template: '{"id": {{ index .Args "id" }}}' - single_function: - function: GetCharacter - upstream: starwars-rest - hero: - gloo_resolver: - single_function: - function: GetHero - upstream: starwars-rest - human: - gloo_resolver: - request_template: '{"id": {{ index .Args "id" }}}' - single_function: - function: GetCharacter - upstream: starwars-rest diff --git a/examples/starwars/sqoop-config/schemas/starwars_schema.yaml b/examples/starwars/sqoop-config/schemas/starwars_schema.yaml deleted file mode 100644 index e792bc5..0000000 --- a/examples/starwars/sqoop-config/schemas/starwars_schema.yaml +++ /dev/null @@ -1,134 +0,0 @@ -name: starwars-schema -resolver_map: starwars-resolvers -inline_schema: | - # The query type, represents all of the entry points into our object graph - type Query { - hero(episode: Episode = NEWHOPE): Character - reviews(episode: Episode!, since: Time): [Review]! - search(text: String!): [SearchResult]! - character(id: ID!): Character - droid(id: ID!): Droid - human(id: ID!): Human - starship(id: ID!): Starship - } - # The mutation type, represents all updates we can make to our data - type Mutation { - createReview(episode: Episode!, review: ReviewInput!): Review - } - # The episodes in the Star Wars trilogy - enum Episode { - # Star Wars Episode IV: A New Hope, released in 1977. - NEWHOPE - # Star Wars Episode V: The Empire Strikes Back, released in 1980. - EMPIRE - # Star Wars Episode VI: Return of the Jedi, released in 1983. - JEDI - } - # A character from the Star Wars universe - interface Character { - # The ID of the character - id: ID! - # The name of the character - name: String! - # The friends of the character, or an empty list if they have none - friends: [Character] - # The friends of the character exposed as a connection with edges - friendsConnection(first: Int, after: ID): FriendsConnection! - # The movies this character appears in - appearsIn: [Episode!]! - } - # Units of height - enum LengthUnit { - # The standard unit around the world - METER - # Primarily used in the United States - FOOT - } - # A humanoid creature from the Star Wars universe - type Human implements Character { - # The ID of the human - id: ID! - # What this human calls themselves - name: String! - # Height in the preferred unit, default is meters - height(unit: LengthUnit = METER): Float! - # Mass in kilograms, or null if unknown - mass: Float - # This human` + "`" + `s friends, or an empty list if they have none - friends: [Character] - # The friends of the human exposed as a connection with edges - friendsConnection(first: Int, after: ID): FriendsConnection! - # The movies this human appears in - appearsIn: [Episode!]! - # A list of starships this person has piloted, or an empty list if none - starships: [Starship] - } - # An autonomous mechanical character in the Star Wars universe - type Droid implements Character { - # The ID of the droid - id: ID! - # What others call this droid - name: String! - # This droid` + "`" + `s friends, or an empty list if they have none - friends: [Character] - # The friends of the droid exposed as a connection with edges - friendsConnection(first: Int, after: ID): FriendsConnection! - # The movies this droid appears in - appearsIn: [Episode!]! - # This droid` + "`" + `s primary function - primaryFunction: String - } - # A connection object for a character` + "`" + `s friends - type FriendsConnection { - # The total number of friends - totalCount: Int! - # The edges for each of the character` + "`" + `s friends. - edges: [FriendsEdge] - # A list of the friends, as a convenience when edges are not needed. - friends: [Character] - # Information for paginating this connection - pageInfo: PageInfo! - } - # An edge object for a character` + "`" + `s friends - type FriendsEdge { - # A cursor used for pagination - cursor: ID! - # The character represented by this friendship edge - node: Character - } - # Information for paginating this connection - type PageInfo { - startCursor: ID! - endCursor: ID! - hasNextPage: Boolean! - } - # Represents a review for a movie - type Review { - # The number of stars this review gave, 1-5 - stars: Int! - # Comment about the movie - commentary: String - # when the review was posted - time: Time - } - # The input object sent when someone is creating a new review - input ReviewInput { - # 0-5 stars - stars: Int! - # Comment about the movie, optional - commentary: String - # when the review was posted - time: Time - } - type Starship { - # The ID of the starship - id: ID! - # The name of the starship - name: String! - # Length of the starship, along the longest axis - length(unit: LengthUnit = METER): Float! - # coordinates tracking this ship - history: [[Int]] - } - union SearchResult = Human | Droid | Starship - scalar Time diff --git a/examples/starwars/starwars-kubernetes.yaml b/examples/starwars/starwars-kubernetes.yaml deleted file mode 100644 index ded290f..0000000 --- a/examples/starwars/starwars-kubernetes.yaml +++ /dev/null @@ -1,290 +0,0 @@ - -########################## -# # -# Example # -# Service # -# # -# # -########################## -# starwars service -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - labels: - app: starwars - name: starwars - namespace: default -spec: - selector: - matchLabels: - app: starwars - replicas: 1 - template: - metadata: - labels: - app: starwars - spec: - containers: - - image: soloio/starwars-example:latest - name: starwars - ports: - - containerPort: 1234 - name: http ---- -apiVersion: v1 -kind: Service -metadata: - name: starwars - namespace: default - labels: - sevice: starwars -spec: - ports: - - port: 1234 - protocol: TCP - selector: - app: starwars - ---- -apiVersion: gloo.solo.io/v1 -kind: Upstream -metadata: - name: starwars-rest - namespace: gloo-system -spec: - spec: - service_name: starwars - service_namespace: default - service_port: 1234 - type: kubernetes - service_info: - type: REST - functions: - - name: GetHero - spec: - headers: - :method: GET - path: /api/hero - - name: GetCharacter - spec: - body: "" - path: /api/character/ - headers: - "x-id": "{{id}}" - :method: GET - - name: GetCharacters - spec: - headers: - :method: POST - path: /api/characters - ---- - -apiVersion: sqoop.solo.io/v1 -kind: Schema -metadata: - name: starwars-rest - namespace: gloo-system -spec: - resolver_map: starwars-rest-resolvers - inline_schema: | - # The query type, represents all of the entry points into our object graph - type Query { - hero(episode: Episode = NEWHOPE): Character - reviews(episode: Episode!, since: Time): [Review]! - search(text: String!): [SearchResult]! - character(id: ID!): Character - droid(id: ID!): Droid - human(id: ID!): Human - starship(id: ID!): Starship - } - # The mutation type, represents all updates we can make to our data - type Mutation { - createReview(episode: Episode!, review: ReviewInput!): Review - } - # The episodes in the Star Wars trilogy - enum Episode { - # Star Wars Episode IV: A New Hope, released in 1977. - NEWHOPE - # Star Wars Episode V: The Empire Strikes Back, released in 1980. - EMPIRE - # Star Wars Episode VI: Return of the Jedi, released in 1983. - JEDI - } - # A character from the Star Wars universe - interface Character { - # The ID of the character - id: ID! - # The name of the character - name: String! - # The friends of the character, or an empty list if they have none - friends: [Character] - # The friends of the character exposed as a connection with edges - friendsConnection(first: Int, after: ID): FriendsConnection! - # The movies this character appears in - appearsIn: [Episode!]! - } - # Units of height - enum LengthUnit { - # The standard unit around the world - METER - # Primarily used in the United States - FOOT - } - # A humanoid creature from the Star Wars universe - type Human implements Character { - # The ID of the human - id: ID! - # What this human calls themselves - name: String! - # Height in the preferred unit, default is meters - height(unit: LengthUnit = METER): Float! - # Mass in kilograms, or null if unknown - mass: Float - # This human` + "`" + `s friends, or an empty list if they have none - friends: [Character] - # The friends of the human exposed as a connection with edges - friendsConnection(first: Int, after: ID): FriendsConnection! - # The movies this human appears in - appearsIn: [Episode!]! - # A list of starships this person has piloted, or an empty list if none - starships: [Starship] - } - # An autonomous mechanical character in the Star Wars universe - type Droid implements Character { - # The ID of the droid - id: ID! - # What others call this droid - name: String! - # This droid` + "`" + `s friends, or an empty list if they have none - friends: [Character] - # The friends of the droid exposed as a connection with edges - friendsConnection(first: Int, after: ID): FriendsConnection! - # The movies this droid appears in - appearsIn: [Episode!]! - # This droid` + "`" + `s primary function - primaryFunction: String - } - # A connection object for a character` + "`" + `s friends - type FriendsConnection { - # The total number of friends - totalCount: Int! - # The edges for each of the character` + "`" + `s friends. - edges: [FriendsEdge] - # A list of the friends, as a convenience when edges are not needed. - friends: [Character] - # Information for paginating this connection - pageInfo: PageInfo! - } - # An edge object for a character` + "`" + `s friends - type FriendsEdge { - # A cursor used for pagination - cursor: ID! - # The character represented by this friendship edge - node: Character - } - # Information for paginating this connection - type PageInfo { - startCursor: ID! - endCursor: ID! - hasNextPage: Boolean! - } - # Represents a review for a movie - type Review { - # The number of stars this review gave, 1-5 - stars: Int! - # Comment about the movie - commentary: String - # when the review was posted - time: Time - } - # The input object sent when someone is creating a new review - input ReviewInput { - # 0-5 stars - stars: Int! - # Comment about the movie, optional - commentary: String - # when the review was posted - time: Time - } - type Starship { - # The ID of the starship - id: ID! - # The name of the starship - name: String! - # Length of the starship, along the longest axis - length(unit: LengthUnit = METER): Float! - # coordinates tracking this ship - history: [[Int]] - } - union SearchResult = Human | Droid | Starship - scalar Time ---- - -apiVersion: sqoop.solo.io/v1 -kind: ResolverMap -metadata: - name: starwars-rest-resolvers - namespace: gloo-system -spec: - types: - Droid: - fields: - appearsIn: {} - friends: {} - friendsConnection: {} - id: {} - name: {} - primaryFunction: {} - FriendsConnection: - fields: - edges: {} - friends: {} - pageInfo: {} - totalCount: {} - FriendsEdge: - fields: - cursor: {} - node: {} - Human: - fields: - appearsIn: {} - friends: {} - friendsConnection: {} - height: {} - id: {} - mass: {} - name: {} - starships: {} - Mutation: - fields: - createReview: {} - PageInfo: - fields: - endCursor: {} - hasNextPage: {} - startCursor: {} - Query: - fields: - character: {} - droid: {} - hero: - gloo_resolver: - single_function: - function: GetHero - upstream: starwars-rest - human: {} - reviews: {} - search: {} - starship: {} - Review: - fields: - commentary: {} - stars: {} - time: {} - Starship: - fields: - history: {} - id: {} - length: {} - name: {} diff --git a/examples/starwars/starwars-rest.go b/examples/starwars/starwars-rest.go deleted file mode 100644 index 190c8f4..0000000 --- a/examples/starwars/starwars-rest.go +++ /dev/null @@ -1,15 +0,0 @@ -package main - -import ( - "fmt" - "log" - "net/http" - - "github.com/solo-io/sqoop/examples/starwars/server" -) - -func main() { - port := 1234 - log.Printf("listening on :%v", port) - log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), server.New())) -} diff --git a/examples/starwars/starwars.schema.graphql b/examples/starwars/starwars.schema.graphql deleted file mode 100644 index 199b530..0000000 --- a/examples/starwars/starwars.schema.graphql +++ /dev/null @@ -1,131 +0,0 @@ -# The query type, represents all of the entry points into our object graph -type Query { - hero(episode: Episode = NEWHOPE): Character - reviews(episode: Episode!, since: Time): [Review]! - search(text: String!): [SearchResult]! - character(id: ID!): Character - droid(id: ID!): Droid - human(id: ID!): Human - starship(id: ID!): Starship -} -# The mutation type, represents all updates we can make to our data -type Mutation { - createReview(episode: Episode!, review: ReviewInput!): Review -} -# The episodes in the Star Wars trilogy -enum Episode { - # Star Wars Episode IV: A New Hope, released in 1977. - NEWHOPE - # Star Wars Episode V: The Empire Strikes Back, released in 1980. - EMPIRE - # Star Wars Episode VI: Return of the Jedi, released in 1983. - JEDI -} -# A character from the Star Wars universe -interface Character { - # The ID of the character - id: ID! - # The name of the character - name: String! - # The friends of the character, or an empty list if they have none - friends: [Character] - # The friends of the character exposed as a connection with edges - friendsConnection(first: Int, after: ID): FriendsConnection! - # The movies this character appears in - appearsIn: [Episode!]! -} -# Units of height -enum LengthUnit { - # The standard unit around the world - METER - # Primarily used in the United States - FOOT -} -# A humanoid creature from the Star Wars universe -type Human implements Character { - # The ID of the human - id: ID! - # What this human calls themselves - name: String! - # Height in the preferred unit, default is meters - height(unit: LengthUnit = METER): Float! - # Mass in kilograms, or null if unknown - mass: Float - # This human` + "`" + `s friends, or an empty list if they have none - friends: [Character] - # The friends of the human exposed as a connection with edges - friendsConnection(first: Int, after: ID): FriendsConnection! - # The movies this human appears in - appearsIn: [Episode!]! - # A list of starships this person has piloted, or an empty list if none - starships: [Starship] -} -# An autonomous mechanical character in the Star Wars universe -type Droid implements Character { - # The ID of the droid - id: ID! - # What others call this droid - name: String! - # This droid` + "`" + `s friends, or an empty list if they have none - friends: [Character] - # The friends of the droid exposed as a connection with edges - friendsConnection(first: Int, after: ID): FriendsConnection! - # The movies this droid appears in - appearsIn: [Episode!]! - # This droid` + "`" + `s primary function - primaryFunction: String -} -# A connection object for a character` + "`" + `s friends -type FriendsConnection { - # The total number of friends - totalCount: Int! - # The edges for each of the character` + "`" + `s friends. - edges: [FriendsEdge] - # A list of the friends, as a convenience when edges are not needed. - friends: [Character] - # Information for paginating this connection - pageInfo: PageInfo! -} -# An edge object for a character` + "`" + `s friends -type FriendsEdge { - # A cursor used for pagination - cursor: ID! - # The character represented by this friendship edge - node: Character -} -# Information for paginating this connection -type PageInfo { - startCursor: ID! - endCursor: ID! - hasNextPage: Boolean! -} -# Represents a review for a movie -type Review { - # The number of stars this review gave, 1-5 - stars: Int! - # Comment about the movie - commentary: String - # when the review was posted - time: Time -} -# The input object sent when someone is creating a new review -input ReviewInput { - # 0-5 stars - stars: Int! - # Comment about the movie, optional - commentary: String - # when the review was posted - time: Time -} -type Starship { - # The ID of the starship - id: ID! - # The name of the starship - name: String! - # Length of the starship, along the longest axis - length(unit: LengthUnit = METER): Float! - # coordinates tracking this ship - history: [[Int]] -} -union SearchResult = Human | Droid | Starship -scalar Time diff --git a/examples/starwars/upstream.yaml b/examples/starwars/upstream.yaml deleted file mode 100644 index f557288..0000000 --- a/examples/starwars/upstream.yaml +++ /dev/null @@ -1,26 +0,0 @@ -name: starwars-rest -type: static -spec: - hosts: - - addr: localhost - port: 9000 -service_info: - type: REST -functions: - - name: GetHero - spec: - headers: - :method: GET - path: /api/hero - - name: GetCharacter - spec: - body: "" - path: /api/character/ - headers: - "x-id": "{{id}}" - :method: GET - - name: GetCharacters - spec: - headers: - :method: POST - path: /api/characters diff --git a/firebase.json b/firebase.json deleted file mode 100644 index 94962d2..0000000 --- a/firebase.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "hosting": { - "target": "sqoop-site", - "public": "site", - "ignore": [ - "firebase.json", - "**/.*", - "**/node_modules/**" - ] - } -} diff --git a/generate.go b/generate.go new file mode 100644 index 0000000..578eac4 --- /dev/null +++ b/generate.go @@ -0,0 +1,24 @@ +package main + +import ( + "github.com/solo-io/solo-kit/pkg/code-generator/cmd" + "github.com/solo-io/solo-kit/pkg/code-generator/docgen/options" + "github.com/solo-io/solo-kit/pkg/utils/log" + "github.com/solo-io/sqoop/version" +) + +//go:generate go run generate.go + +func main() { + err := version.CheckVersions() + if err != nil { + log.Fatalf("generate failed!: %v", err) + } + docsOpts := cmd.DocsOptions{ + Output: options.Hugo, + } + log.Printf("starting generate") + if err := cmd.Run(".", true, &docsOpts, nil, nil); err != nil { + log.Fatalf("generate failed!: %v", err) + } +} diff --git a/hack/create-release.sh b/hack/create-release.sh deleted file mode 100755 index fbbf5f5..0000000 --- a/hack/create-release.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash - -CONFIG=$@ - -for line in $CONFIG; do - eval "export ${line}" -done - -# Define variables. -GH_API="https://api.github.com" -GH_REPO="$GH_API/repos/$owner/$repo" -GH_TAGS="$GH_REPO/releases/tags/$tag" -AUTH="Authorization: token $github_api_token" -WGET_ARGS="--content-disposition --auth-no-challenge --no-cookie" -CURL_ARGS="-LJO#" - -if [[ "$tag" == 'LATEST' ]]; then - GH_TAGS="$GH_REPO/releases/latest" -fi - -# Validate token. -curl -o /dev/null -sH "$AUTH" $GH_REPO || { echo "Error: Invalid repo, token or network issue!"; exit 1; } - - - -BODY=$(cat <${HOME}/.glooctl/config.yaml << EOF -FileOptions: - ConfigDir: ${PWD}/_gloo_config - FilesDir: ${PWD}/_gloo_config/files - SecretDir: ${PWD}/_gloo_config/files -ConfigStorageOptions: - SyncFrequency: 1000000000 - Type: file -FileStorageOptions: - SyncFrequency: 1000000 - Type: file -SecretStorageOptions: - SyncFrequency: 100000 - Type: file -EOF -} - -echo "creating gloo storage directories" -mkdir -p ./_gloo_config/{upstreams,virtualservices,roles,secrets,files,resolver_maps,schemas} - -mkdir -p ${HOME}/.glooctl/ - -if [ -f ${HOME}/.glooctl/config.yaml ]; then - echo -n "Do you wish to edit ${HOME}/.glooctl/config.yaml to set defaults to docker-compose storage (Y/N)? " - ### using it - if asksure; then - echo "Modifying ${HOME}/.glooctl/config.yaml" - overwrite - fi -fi - diff --git a/install/helm/sqoop/.helmignore b/install/helm/sqoop/.helmignore new file mode 100644 index 0000000..990dcdf --- /dev/null +++ b/install/helm/sqoop/.helmignore @@ -0,0 +1,27 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj + +# template files +*-template.yaml +# generator files +*.go +generate/ diff --git a/install/helm/sqoop/Chart-template.yaml b/install/helm/sqoop/Chart-template.yaml new file mode 100644 index 0000000..8c7c891 --- /dev/null +++ b/install/helm/sqoop/Chart-template.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: sqoop diff --git a/install/helm/sqoop/generate.go b/install/helm/sqoop/generate.go new file mode 100644 index 0000000..c6a8f3d --- /dev/null +++ b/install/helm/sqoop/generate.go @@ -0,0 +1,166 @@ +package main + +import ( + "fmt" + "io/ioutil" + "os" + + "github.com/ghodss/yaml" + toml "github.com/pelletier/go-toml" + "github.com/pkg/errors" + glooGenerate "github.com/solo-io/gloo/install/helm/gloo/generate" + "github.com/solo-io/solo-kit/pkg/utils/log" + "github.com/solo-io/sqoop/install/helm/sqoop/generate" +) + +const ( + valuesTemplate = "install/helm/sqoop/values-template.yaml" + valuesOutput = "install/helm/sqoop/values.yaml" + chartTemplate = "install/helm/sqoop/Chart-template.yaml" + chartOutput = "install/helm/sqoop/Chart.yaml" + requirementsTemplate = "install/helm/sqoop/requirements-template.yaml" + requirementsOutput = "install/helm/sqoop/requirements.yaml" + + gopkgToml = "Gopkg.toml" + constraint = "constraint" + glooPkg = "github.com/solo-io/gloo" + nameConst = "name" + versionConst = "version" + neverPull = "Never" + alwaysPull = "Always" + ifNotPresent = "IfNotPresent" +) + +func main() { + var version string + if len(os.Args) < 2 { + panic("Must provide version as argument") + } else { + version = os.Args[1] + } + log.Printf("Generating helm files.") + if err := generateValuesYamls(version); err != nil { + log.Fatalf("generating values.yaml failed!: %v", err) + } + if err := generateChartYaml(version); err != nil { + log.Fatalf("generating Chart.yaml failed!: %v", err) + } + if err := generateRequirementsYaml(); err != nil { + log.Fatalf("unable to parse Gopkg.toml for proper gloo version: %v", err) + } +} + +func readConfig(path string) (generate.Config, error) { + var config generate.Config + if err := readYaml(valuesTemplate, &config); err != nil { + return config, err + } + return config, nil +} + +func readYaml(path string, obj interface{}) error { + bytes, err := ioutil.ReadFile(path) + if err != nil { + return errors.Wrapf(err, "failed reading server config file: %s", path) + } + + if err := yaml.Unmarshal(bytes, obj); err != nil { + return errors.Wrap(err, "failed parsing configuration file") + } + + return nil +} + +func writeYaml(obj interface{}, path string) error { + bytes, err := yaml.Marshal(obj) + if err != nil { + return errors.Wrapf(err, "failed marshaling config struct") + } + + err = ioutil.WriteFile(path, bytes, os.ModePerm) + if err != nil { + return errors.Wrapf(err, "failing writing config file") + } + return nil +} + +func generateValuesYaml(version, pullPolicy, outputFile string) error { + config, err := readConfig(valuesTemplate) + if err != nil { + return err + } + config.Sqoop.Deployment.Image.Tag = version + + config.Sqoop.Deployment.Image.PullPolicy = pullPolicy + + return writeYaml(&config, outputFile) +} + +func generateValuesYamls(version string) error { + // Generate values for standard manifest + standardPullPolicy := alwaysPull + if version == "dev" { + standardPullPolicy = neverPull + } + if err := generateValuesYaml(version, standardPullPolicy, valuesOutput); err != nil { + return err + } + + return nil +} + +func generateChartYaml(version string) error { + var chart glooGenerate.Chart + if err := readYaml(chartTemplate, &chart); err != nil { + return err + } + + chart.Version = version + + return writeYaml(&chart, chartOutput) +} + +func generateRequirementsYaml() error { + var dl generate.DependencyList + if err := readYaml(requirementsTemplate, &dl); err != nil { + return err + } + glooVersion, err := parseToml() + if err != nil { + return err + } + for i, v := range dl.Dependencies { + if v.Name == "gloo" { + dl.Dependencies[i].Version = glooVersion + } + } + return writeYaml(dl, requirementsOutput) +} + +func parseToml() (string, error) { + config, err := toml.LoadFile(gopkgToml) + if err != nil { + return "", err + } + + tomlTree := config.Get(constraint) + var ( + tree []*toml.Tree + version string + ) + + switch typedTree := tomlTree.(type) { + case []*toml.Tree: + tree = typedTree + default: + return "", fmt.Errorf("unable to parse toml tree") + } + + for _, v := range tree { + if v.Get(nameConst) == glooPkg && v.Get(versionConst) != "" { + version = v.Get(versionConst).(string) + } + } + + return version, nil +} diff --git a/install/helm/sqoop/generate/requirements.go b/install/helm/sqoop/generate/requirements.go new file mode 100644 index 0000000..8a9bff3 --- /dev/null +++ b/install/helm/sqoop/generate/requirements.go @@ -0,0 +1,11 @@ +package generate + +type Dependency struct { + Name string `json:"name"` + Version string `json:"version"` + Repository string `json:"repository"` +} + +type DependencyList struct { + Dependencies []Dependency `json:"dependencies"` +} diff --git a/install/helm/sqoop/generate/values.go b/install/helm/sqoop/generate/values.go new file mode 100644 index 0000000..c0ca994 --- /dev/null +++ b/install/helm/sqoop/generate/values.go @@ -0,0 +1,42 @@ +package generate + +import "github.com/solo-io/gloo/install/helm/gloo/generate" + +type Config struct { + Gloo *generate.Config `json:"gloo,omitempty"` + Sqoop *Sqoop `json:"sqoop,omitempty"` +} + +// Common + +type OAuth struct { + Server string `json:"server"` + Client string `json:"client"` +} + +type Rbac struct { + Create bool `json:"create"` +} + +// Sqoop + +type Sqoop struct { + Deployment *SqoopDeployment `json:"deployment,omitempty"` + Service *SqoopService `json:"service,omitempty"` + ConfigMap *SqoopConfigMap `json:"configMap,omitempty"` +} + +type SqoopDeployment struct { + Image *generate.Image `json:"image,omitempty"` + Proxy *generate.Image `json:"proxy,omitempty"` + *generate.DeploymentSpec +} + +type SqoopService struct { + Port string `json:"port"` + Name string `json:"name"` +} + +type SqoopConfigMap struct { + Name string `json:"name"` +} diff --git a/install/helm/sqoop/requirements-template.yaml b/install/helm/sqoop/requirements-template.yaml new file mode 100644 index 0000000..73e7400 --- /dev/null +++ b/install/helm/sqoop/requirements-template.yaml @@ -0,0 +1,3 @@ +dependencies: + - name: gloo + repository: https://storage.googleapis.com/solo-public-helm \ No newline at end of file diff --git a/install/helm/sqoop/templates/0-sqoop-service.yaml b/install/helm/sqoop/templates/0-sqoop-service.yaml new file mode 100644 index 0000000..d3d47f9 --- /dev/null +++ b/install/helm/sqoop/templates/0-sqoop-service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app: {{ .Values.sqoop.service.name }} + gloo: {{ .Values.sqoop.service.name }} + name: {{ .Values.sqoop.service.name }} + namespace: {{ .Release.Namespace }} +spec: + type: LoadBalancer + ports: + - name: http + port: {{ .Values.sqoop.service.port }} + protocol: TCP + selector: + gloo: {{ .Values.sqoop.service.name }} \ No newline at end of file diff --git a/install/helm/sqoop/templates/1-sqoop-deployment.yaml b/install/helm/sqoop/templates/1-sqoop-deployment.yaml new file mode 100644 index 0000000..bcfebc9 --- /dev/null +++ b/install/helm/sqoop/templates/1-sqoop-deployment.yaml @@ -0,0 +1,53 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: sqoop + namespace: {{ .Release.Namespace }} +spec: + replicas: 1 + selector: + matchLabels: + gloo: sqoop + template: + metadata: + labels: + gloo: sqoop + spec: + containers: + - image: "{{ .Values.sqoop.deployment.image.repository }}:{{ .Values.sqoop.deployment.image.tag }}" + imagePullPolicy: {{ .Values.sqoop.deployment.image.pullPolicy }} + name: sqoop + {{- if .Values.sqoop.deployment.stats }} + env: + - name: START_STATS_SERVER + value: "true" + {{- end}} + ports: + - containerPort: {{ .Values.sqoop.service.port }} + - image: "{{ .Values.sqoop.deployment.proxy.repository }}:{{ .Values.sqoop.deployment.proxy.tag }}" + imagePullPolicy: {{ .Values.sqoop.deployment.image.pullPolicy }} + args: ["--disable-hot-restart"] + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + name: envoy + ports: + - containerPort: 8080 + name: http + - containerPort: 8443 + name: https + - containerPort: 19000 + name: admin + volumeMounts: + - name: {{ .Values.sqoop.configMap.name }} + mountPath: /etc/envoy + volumes: + - name: {{ .Values.sqoop.configMap.name }} + configMap: + name: {{ .Values.sqoop.configMap.name }} diff --git a/install/helm/sqoop/templates/2-sqoop-clusterrole.yaml b/install/helm/sqoop/templates/2-sqoop-clusterrole.yaml new file mode 100644 index 0000000..b551512 --- /dev/null +++ b/install/helm/sqoop/templates/2-sqoop-clusterrole.yaml @@ -0,0 +1,9 @@ + +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: sqoop-role +rules: +- apiGroups: ["sqoop.solo.io"] + resources: ["schemas", "resolvermaps"] + verbs: ["*"] diff --git a/install/helm/sqoop/templates/3-sqoop-clusterrole-binding.yaml b/install/helm/sqoop/templates/3-sqoop-clusterrole-binding.yaml new file mode 100644 index 0000000..e5a0296 --- /dev/null +++ b/install/helm/sqoop/templates/3-sqoop-clusterrole-binding.yaml @@ -0,0 +1,14 @@ + +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: sqoop-role-binding +subjects: + - kind: ServiceAccount + name: default + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: sqoop-role + apiGroup: rbac.authorization.k8s.io + diff --git a/install/helm/sqoop/templates/4-sqoop-envoy-configmap.yaml b/install/helm/sqoop/templates/4-sqoop-envoy-configmap.yaml new file mode 100644 index 0000000..7d769df --- /dev/null +++ b/install/helm/sqoop/templates/4-sqoop-envoy-configmap.yaml @@ -0,0 +1,46 @@ +# config_map +apiVersion: v1 +data: + envoy.yaml: | + node: + cluster: sqoop + id: "{{ "{{" }}.PodName{{ "}}" }}.{{ "{{" }}.PodNamespace{{ "}}" }}" + metadata: + # this line must match ! + role: "{{ .Release.Namespace }}~sqoop-proxy" + static_resources: + clusters: + - name: xds_cluster + connect_timeout: 5.000s + load_assignment: + cluster_name: xds_cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: gloo + port_value: {{ .Values.gloo.gloo.deployment.xdsPort }} + http2_protocol_options: {} + type: STRICT_DNS + dynamic_resources: + ads_config: + api_type: GRPC + grpc_services: + - envoy_grpc: {cluster_name: xds_cluster} + cds_config: + ads: {} + lds_config: + ads: {} + admin: + access_log_path: /dev/null + address: + socket_address: + address: 127.0.0.1 + port_value: 19000 +kind: ConfigMap +metadata: + labels: + app: {{.Release.Name}} + name: {{ .Values.sqoop.configMap.name }} + namespace: {{ .Release.Namespace }} diff --git a/install/helm/sqoop/values-template.yaml b/install/helm/sqoop/values-template.yaml new file mode 100644 index 0000000..344f24c --- /dev/null +++ b/install/helm/sqoop/values-template.yaml @@ -0,0 +1,22 @@ +######################################################################## +### ### +### Sqoop ### +### ### +######################################################################## + + +sqoop: + deployment: + name: sqoop + image: + repository: soloio/sqoop + pullPolicy: Always + proxy: + tag: 0.6.19 + repository: soloio/gloo-envoy-wrapper + pullPolicy: Always + service: + port: 9095 + name: sqoop + configMap: + name: sqoop-proxy-config diff --git a/install/kube/README.md b/install/kube/README.md deleted file mode 100644 index 7fce2dd..0000000 --- a/install/kube/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# Quick minikube instructions: - -On linux, the easiest way to start is with the kvm2 driver: -``` -minikube stop; \ -minikube start --vm-driver=kvm2 \ - --feature-gates=AdvancedAudit=true \ - --extra-config=apiserver.Authorization.Mode=RBAC \ - --extra-config=apiserver.Audit.LogOptions.Path=/hosthome/$USER/.minikube/logs/audit.log \ - --extra-config=apiserver.Audit.MaxAge=30 \ - --extra-config=apiserver.Audit.MaxSize=100 \ - --extra-config=apiserver.Audit.MaxBackups=5 \ - --extra-config=apiserver.Audit.PolicyFile=/etc/kubernetes/addons/audit-policy.yaml && \ -kubectl create clusterrolebinding permissive-binding \ - --clusterrole=cluster-admin \ - --user=admin \ - --user=kubelet \ - --group=system:serviceaccounts - - -minikube stop; minikube start --vm-driver=kvm2 --feature-gates=AdvancedAudit=true --extra-config=apiserver.Authorization.Mode=RBAC --extra-config=apiserver.Audit.LogOptions.Path=/hosthome/$USER/.minikube/logs/audit.log --extra-config=apiserver.Audit.PolicyFile=/etc/kubernetes/addons/audit-policy.yaml && kubectl create clusterrolebinding permissive-binding \ - --clusterrole=cluster-admin \ - --user=admin \ - --user=kubelet \ - --group=system:serviceaccounts -``` - -To install Sqoop and Gloo: -``` -kubectl apply -f install.yaml -``` -To access: -``` -export QLOO_URL=http://$(minikube ip):$(kubectl get svc sqoop -n gloo-system -o 'jsonpath={.spec.ports[?(@.name=="http")].nodePort}') -``` - - -And open in your browser: -``` -xdg-open $QLOO_URL -``` diff --git a/install/kube/install.yaml b/install/kube/install.yaml deleted file mode 100644 index a1fbbd5..0000000 --- a/install/kube/install.yaml +++ /dev/null @@ -1,434 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: schemas.sqoop.solo.io -spec: - group: sqoop.solo.io - names: - kind: Schema - listKind: SchemaList - plural: schemas - singular: schema - scope: Namespaced - version: v1 - ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: resolvermaps.sqoop.solo.io -spec: - group: sqoop.solo.io - names: - kind: ResolverMap - listKind: ResolverMapList - plural: resolvermaps - singular: resolvermap - scope: Namespaced - version: v1 - ---- -#rbac for function-discovery -kind: ClusterRole -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: sqoop-role -rules: -- apiGroups: ["sqoop.solo.io"] - resources: ["schemas", "resolvermaps"] - verbs: ["*"] -- apiGroups: ["gloo.solo.io"] - resources: ["virtualservices"] - verbs: ["*"] - ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: sqoop-cluster-binding -subjects: -- kind: ServiceAccount - name: default - namespace: gloo-system -roleRef: - kind: ClusterRole - name: sqoop-role - apiGroup: rbac.authorization.k8s.io - ---- - -apiVersion: apps/v1beta2 -kind: Deployment -metadata: - name: sqoop - namespace: gloo-system - labels: - gloo: sqoop -spec: - replicas: 1 - selector: - matchLabels: - gloo: sqoop - template: - metadata: - labels: - gloo: sqoop - spec: - initContainers: - - name: assignnodeid - image: busybox - command: - - sed - - s/NODE_ID_PLACE_HOLDER/$(POD_NAME).$(POD_NAMESPACE)/;w /config-tmp/envoy.yaml - - "/config/envoy.yaml" - volumeMounts: - - name: sqoop-proxy-config - mountPath: /config - - name: config-tmp - mountPath: /config-tmp - env: - - name: POD_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.namespace - containers: - - name: proxy - image: "soloio/envoy:0.4.4" - imagePullPolicy: IfNotPresent - ports: - - containerPort: 8080 - name: http - - containerPort: 8443 - name: https - - containerPort: 19000 - name: admin - command: ["envoy"] - args: ["-c", "/config/envoy.yaml", "--v2-config-only"] - volumeMounts: - - name: config-tmp - mountPath: /config - - name: sqoop - image: "soloio/sqoop:0.1.1" - imagePullPolicy: IfNotPresent - ports: - - containerPort: 9090 - name: http - args: - - "--storage.type=kube" - - "--storage.refreshrate=1m" - - "--kube.namespace=gloo-system" - volumeMounts: - - name: config-tmp - mountPath: /config - volumes: - - name: sqoop-proxy-config - configMap: - name: sqoop-proxy-config - - name: config-tmp - emptyDir: {} ---- -# GW -apiVersion: v1 -kind: Service -metadata: - name: sqoop - namespace: gloo-system - labels: - gloo: sqoop -spec: - type: LoadBalancer - ports: - - port: 9090 - protocol: TCP - name: http - selector: - gloo: sqoop - - ---- - -apiVersion: v1 -kind: ConfigMap -metadata: - name: sqoop-proxy-config - namespace: gloo-system -data: - envoy.yaml: | - node: - cluster: sqoop - id: sqoop~NODE_ID_PLACE_HOLDER - static_resources: - clusters: - - name: xds_cluster - connect_timeout: 5.000s - hosts: - - socket_address: - address: control-plane - port_value: 8081 - http2_protocol_options: {} - type: STRICT_DNS - dynamic_resources: - ads_config: - api_type: GRPC - grpc_services: - - envoy_grpc: {cluster_name: xds_cluster} - cds_config: - ads: {} - lds_config: - ads: {} - admin: - access_log_path: /dev/null - address: - socket_address: - address: 0.0.0.0 - port_value: 19000 - - -### Gloo - ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: upstreams.gloo.solo.io -spec: - group: gloo.solo.io - names: - kind: Upstream - listKind: UpstreamList - plural: upstreams - singular: upstream - scope: Namespaced - version: v1 - ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: virtualservices.gloo.solo.io -spec: - group: gloo.solo.io - names: - kind: VirtualService - listKind: VirtualServiceList - plural: virtualservices - singular: virtualservice - scope: Namespaced - version: v1 ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: roles.gloo.solo.io -spec: - group: gloo.solo.io - names: - kind: Role - listKind: RoleList - plural: roles - singular: role - scope: Namespaced - version: v1 - ---- -# Source: gloo/templates/rbac.yaml ---- -#rbac for control-plane -kind: ClusterRole -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: gloo-role -rules: -- apiGroups: [""] - resources: ["pods", "services", "secrets", "endpoints", "configmaps"] - verbs: ["get", "watch", "list"] -- apiGroups: [""] - resources: ["namespaces"] - verbs: ["get", "create"] -- apiGroups: ["apiextensions.k8s.io"] - resources: ["customresourcedefinitions"] - verbs: ["get", "create"] -- apiGroups: ["gloo.solo.io"] - resources: ["upstreams", "virtualservices", "roles"] - verbs: ["*"] ---- -#rbac for function-discovery -kind: ClusterRole -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: gloo-discovery-role -rules: -- apiGroups: [""] - resources: ["pods", "services", "endpoints"] - verbs: ["get", "watch", "list"] -- apiGroups: [""] - resources: ["secrets", "configmaps"] - verbs: ["*"] -- apiGroups: ["extensions"] - resources: ["ingresses"] - verbs: ["get", "watch", "list"] -- apiGroups: ["gloo.solo.io"] - resources: ["upstreams", "virtualservices"] - verbs: ["*"] - ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: gloo-cluster-admin-binding -subjects: -- kind: ServiceAccount - name: default - namespace: gloo-system -roleRef: - kind: ClusterRole - name: gloo-role - apiGroup: rbac.authorization.k8s.io - ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: gloo-discovery-cluster-admin-binding -subjects: -- kind: ServiceAccount - name: default - namespace: gloo-system -roleRef: - kind: ClusterRole - name: gloo-discovery-role - apiGroup: rbac.authorization.k8s.io - ---- -# Source: gloo/templates/control-plane.yaml -apiVersion: apps/v1beta2 -kind: Deployment -metadata: - name: control-plane - namespace: gloo-system - labels: - gloo: control-plane -spec: - replicas: 1 - selector: - matchLabels: - gloo: control-plane - template: - metadata: - labels: - gloo: control-plane - spec: - containers: - - name: control-plane - image: "soloio/control-plane:0.4.4" - imagePullPolicy: IfNotPresent - ports: - - containerPort: 8081 - name: http - env: - - name: GRPC_TRACE - value: "all" - - name: DEBUG - value: "1" - args: - - "--storage.type=kube" - - "--storage.refreshrate=1m" - - "--secrets.type=kube" - - "--secrets.refreshrate=1m" - - "--files.type=kube" - - "--files.refreshrate=1m" - - "--xds.port=8081" - - "--kube.namespace=gloo-system" ---- -apiVersion: v1 -kind: Service -metadata: - name: control-plane - namespace: gloo-system - labels: - gloo: control-plane -spec: - ports: - - port: 8081 - protocol: TCP - name: http - selector: - gloo: control-plane ---- -# Source: gloo/templates/function-discovery.yaml - -apiVersion: apps/v1beta2 -kind: Deployment -metadata: - name: function-discovery - namespace: gloo-system - labels: - gloo: function-discovery -spec: - replicas: 1 - selector: - matchLabels: - gloo: function-discovery - template: - metadata: - labels: - gloo: function-discovery - spec: - containers: - - name: function-discovery - image: "soloio/function-discovery:0.4.4" - imagePullPolicy: IfNotPresent - env: - - name: GRPC_TRACE - value: "all" - - name: DEBUG - value: "1" - args: - - "--storage.type=kube" - - "--storage.refreshrate=30m" - - "--secrets.type=kube" - - "--secrets.refreshrate=30m" - - "--files.type=kube" - - "--files.refreshrate=30m" - - "--kube.namespace=gloo-system" - ---- -# Source: gloo/templates/upstream-discovery.yaml - -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - labels: - gloo: upstream-discovery - name: upstream-discovery - namespace: gloo-system -spec: - selector: - matchLabels: - gloo: upstream-discovery - replicas: 1 - template: - metadata: - labels: - gloo: upstream-discovery - spec: - containers: - - image: soloio/upstream-discovery:0.4.4 - imagePullPolicy: IfNotPresent - name: upstream-discovery - env: - - name: GRPC_TRACE - value: "all" - - name: DEBUG - value: "1" - args: - - "--storage.type=kube" - - "--storage.refreshrate=30m" - - "--kube.namespace=gloo-system" diff --git a/mkdocs.yml b/mkdocs.yml deleted file mode 100644 index 9bdc254..0000000 --- a/mkdocs.yml +++ /dev/null @@ -1,38 +0,0 @@ -site_name: Sqoop -pages: - - Index: index.md - - Introduction: - - Introduction: introduction/introduction.md - - Concepts: - - API Objects: introduction/concepts/api_objects.md - - Resolvers: introduction/concepts/resolvers.md - - Architecture: introduction/architecture.md - - Installation: - - Installing on Docker: installation/docker.md - - Installing on Kubernetes: installation/kubernetes.md - - Getting Started: -# - Docker: -# - Basic Routing: getting_started/docker/1.md -# - Function Routing: getting_started/docker/2.md - - Docker: getting_started/docker/1.md - - Kubernetes: getting_started/kubernetes/1.md -# - AWS: -# - Basic Lambda with Gloo: getting_started/aws/lambda.md -# - Dev Guide: dev/README.md -# - Tutorials: -# - Advanced: -# - Bootstrap Options: advanced/bootstrap_options.md - - v1 API reference: - - Schemas: v1/schema.md - - ResolverMaps: v1/resolver_map.md - - Config: v1/config.md -repo_url: https://github.com/solo-io/sqoop/ -site_author: gloo Project Authors -copyright: © Copyright 2018, solo.io Inc. -google_analytics: ['UA-101654320-1', 'sqoop.solo.io'] -theme: - name: 'material' - favicon: 'img/favicon.ico' -extra_css: [extra.css] -extra: - version: 0.1.1 diff --git a/pin_repos.go b/pin_repos.go new file mode 100644 index 0000000..097f1b3 --- /dev/null +++ b/pin_repos.go @@ -0,0 +1,27 @@ +package main + +import ( + version "github.com/solo-io/go-utils/versionutils" + "github.com/solo-io/solo-kit/pkg/utils/log" +) + +func main() { + tomlTree, err := version.ParseToml() + fatalCheck(err, "parsing error") + + glooVersion, err := version.GetVersion(version.GlooPkg, tomlTree) + fatalCheck(err, "getting gloo version") + + soloKitVersion, err := version.GetVersion(version.SoloKitPkg, tomlTree) + fatalCheck(err, "getting solo-kit version") + + fatalCheck(version.PinGitVersion("../gloo", glooVersion), "consider git fetching in gloo repo") + + fatalCheck(version.PinGitVersion("../solo-kit", soloKitVersion), "consider git fetching in solo-kit repo") +} + +func fatalCheck(err error, msg string) { + if err != nil { + log.Fatalf("Error (%v) unable to pin repos!: %v", msg, err) + } +} \ No newline at end of file diff --git a/pkg/api/types/v1/config.pb.go b/pkg/api/types/v1/config.pb.go deleted file mode 100644 index a02bee7..0000000 --- a/pkg/api/types/v1/config.pb.go +++ /dev/null @@ -1,127 +0,0 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: config.proto - -/* -Package v1 is a generated protocol buffer package. - -It is generated from these files: - config.proto - resolver_map.proto - schema.proto - -It has these top-level messages: - Config - ResolverMap - TypeResolver - Resolver - GlooResolver - Function - MultiFunction - WeightedFunction - TemplateResolver - NodeJSResolver - Schema -*/ -package v1 - -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import _ "github.com/gogo/protobuf/gogoproto" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package - -// * -// Config is a top-level config object. It is used internally by Sqoop as a container for the entire set of config objects. -type Config struct { - Schemas []*Schema `protobuf:"bytes,3,rep,name=schemas" json:"schemas,omitempty"` - ResolverMaps []*ResolverMap `protobuf:"bytes,4,rep,name=resolver_maps,json=resolverMaps" json:"resolver_maps,omitempty"` -} - -func (m *Config) Reset() { *m = Config{} } -func (m *Config) String() string { return proto.CompactTextString(m) } -func (*Config) ProtoMessage() {} -func (*Config) Descriptor() ([]byte, []int) { return fileDescriptorConfig, []int{0} } - -func (m *Config) GetSchemas() []*Schema { - if m != nil { - return m.Schemas - } - return nil -} - -func (m *Config) GetResolverMaps() []*ResolverMap { - if m != nil { - return m.ResolverMaps - } - return nil -} - -func init() { - proto.RegisterType((*Config)(nil), "sqoop.api.v1.Config") -} -func (this *Config) Equal(that interface{}) bool { - if that == nil { - return this == nil - } - - that1, ok := that.(*Config) - if !ok { - that2, ok := that.(Config) - if ok { - that1 = &that2 - } else { - return false - } - } - if that1 == nil { - return this == nil - } else if this == nil { - return false - } - if len(this.Schemas) != len(that1.Schemas) { - return false - } - for i := range this.Schemas { - if !this.Schemas[i].Equal(that1.Schemas[i]) { - return false - } - } - if len(this.ResolverMaps) != len(that1.ResolverMaps) { - return false - } - for i := range this.ResolverMaps { - if !this.ResolverMaps[i].Equal(that1.ResolverMaps[i]) { - return false - } - } - return true -} - -func init() { proto.RegisterFile("config.proto", fileDescriptorConfig) } - -var fileDescriptorConfig = []byte{ - // 198 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x49, 0xce, 0xcf, 0x4b, - 0xcb, 0x4c, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x2e, 0xcc, 0xc9, 0xcf, 0xd7, 0x4b, - 0x2c, 0xc8, 0xd4, 0x2b, 0x33, 0x94, 0xe2, 0x29, 0x4e, 0xce, 0x48, 0xcd, 0x4d, 0x84, 0x48, 0x49, - 0x09, 0x15, 0xa5, 0x16, 0xe7, 0xe7, 0x94, 0xa5, 0x16, 0xc5, 0xe7, 0x26, 0x16, 0x40, 0xc5, 0x44, - 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0x4c, 0x7d, 0x10, 0x0b, 0x22, 0xaa, 0x54, 0xc6, 0xc5, 0xe6, 0x0c, - 0x36, 0x54, 0x48, 0x97, 0x8b, 0x1d, 0x62, 0x46, 0xb1, 0x04, 0xb3, 0x02, 0xb3, 0x06, 0xb7, 0x91, - 0xb0, 0x1e, 0x92, 0x05, 0x7a, 0xc1, 0x60, 0xb9, 0x20, 0x98, 0x1a, 0x21, 0x5b, 0x2e, 0x5e, 0x64, - 0x4b, 0x8a, 0x25, 0x58, 0xc0, 0x9a, 0x24, 0x50, 0x34, 0x05, 0x41, 0x55, 0xf8, 0x26, 0x16, 0x04, - 0xf1, 0x14, 0x21, 0x38, 0xc5, 0x4e, 0x7a, 0x2b, 0x1e, 0xc9, 0x31, 0x46, 0x69, 0xa4, 0x67, 0x96, - 0x64, 0x94, 0x26, 0xe9, 0x25, 0xe7, 0xe7, 0xea, 0x17, 0xe7, 0xe7, 0xe4, 0xeb, 0x66, 0xe6, 0xeb, - 0x83, 0xf4, 0xeb, 0x17, 0x64, 0xa7, 0xeb, 0x27, 0x16, 0x64, 0xea, 0x97, 0x54, 0x16, 0xa4, 0x16, - 0xeb, 0x97, 0x19, 0x26, 0xb1, 0x81, 0x9d, 0x6b, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x1e, 0xd6, - 0x87, 0x78, 0x03, 0x01, 0x00, 0x00, -} diff --git a/pkg/api/types/v1/interface.go b/pkg/api/types/v1/interface.go deleted file mode 100644 index b14428b..0000000 --- a/pkg/api/types/v1/interface.go +++ /dev/null @@ -1,36 +0,0 @@ -package v1 - -import ( - "github.com/solo-io/gloo/pkg/api/types/v1" -) - -// implement v1.ConfigObject - -var _ v1.ConfigObject = &Schema{} -var _ v1.ConfigObject = &ResolverMap{} - -// because proto refuses to do setters - -func (item *Schema) SetName(name string) { - item.Name = name -} - -func (item *Schema) SetStatus(status *v1.Status) { - item.Status = status -} - -func (item *Schema) SetMetadata(meta *v1.Metadata) { - item.Metadata = meta -} - -func (item *ResolverMap) SetName(name string) { - item.Name = name -} - -func (item *ResolverMap) SetStatus(status *v1.Status) { - item.Status = status -} - -func (item *ResolverMap) SetMetadata(meta *v1.Metadata) { - item.Metadata = meta -} diff --git a/pkg/api/types/v1/resolver_map.pb.go b/pkg/api/types/v1/resolver_map.pb.go deleted file mode 100644 index 599cbbf..0000000 --- a/pkg/api/types/v1/resolver_map.pb.go +++ /dev/null @@ -1,957 +0,0 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: resolver_map.proto - -package v1 - -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import _ "github.com/gogo/protobuf/gogoproto" -import gloo_api_v1 "github.com/solo-io/gloo/pkg/api/types/v1" -import gloo_api_v11 "github.com/solo-io/gloo/pkg/api/types/v1" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// -// The ResolverMap object maps Resolvers to the fields in the GraphQL Schema -// The skeleton of a Resolver Map will be generated by Sqoop automatically when a schema -// is read or updated if one does not alreay exist. -type ResolverMap struct { - // Name of the Resolver Map. Resolver Map names must be unique - // - // Resolver Map Names must be unique and follow the following syntax rules: - // One or more lowercase rfc1035/rfc1123 labels separated by '.' with a maximum length of 253 characters. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // Types is a map of Type Names (defined in the schema) to a TypeResolver, which contain resolvers for the - // specific fields of the type - Types map[string]*TypeResolver `protobuf:"bytes,3,rep,name=types" json:"types,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"` - // Status indicates the validation status of the role resource. - // Status is read-only by clients, and set by gloo during validation - Status *gloo_api_v1.Status `protobuf:"bytes,4,opt,name=status" json:"status,omitempty" testdiff:"ignore"` - // Metadata contains the resource metadata for the role - Metadata *gloo_api_v11.Metadata `protobuf:"bytes,5,opt,name=metadata" json:"metadata,omitempty"` -} - -func (m *ResolverMap) Reset() { *m = ResolverMap{} } -func (m *ResolverMap) String() string { return proto.CompactTextString(m) } -func (*ResolverMap) ProtoMessage() {} -func (*ResolverMap) Descriptor() ([]byte, []int) { return fileDescriptorResolverMap, []int{0} } - -func (m *ResolverMap) GetName() string { - if m != nil { - return m.Name - } - return "" -} - -func (m *ResolverMap) GetTypes() map[string]*TypeResolver { - if m != nil { - return m.Types - } - return nil -} - -func (m *ResolverMap) GetStatus() *gloo_api_v1.Status { - if m != nil { - return m.Status - } - return nil -} - -func (m *ResolverMap) GetMetadata() *gloo_api_v11.Metadata { - if m != nil { - return m.Metadata - } - return nil -} - -// TypeResolver contains the individual resolvers for each field for a specific type -type TypeResolver struct { - // This is a map of Field Names to the resolver that Sqoop should invoke when a query arrives for that field - Fields map[string]*Resolver `protobuf:"bytes,1,rep,name=fields" json:"fields,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"` -} - -func (m *TypeResolver) Reset() { *m = TypeResolver{} } -func (m *TypeResolver) String() string { return proto.CompactTextString(m) } -func (*TypeResolver) ProtoMessage() {} -func (*TypeResolver) Descriptor() ([]byte, []int) { return fileDescriptorResolverMap, []int{1} } - -func (m *TypeResolver) GetFields() map[string]*Resolver { - if m != nil { - return m.Fields - } - return nil -} - -// Resolvers define the actual logic Sqoop needs to know in order to resolve a specific field query -type Resolver struct { - // a resolver can have one of three types: - // - // Types that are valid to be assigned to Resolver: - // *Resolver_GlooResolver - // *Resolver_TemplateResolver - // *Resolver_NodejsResolver - Resolver isResolver_Resolver `protobuf_oneof:"resolver"` -} - -func (m *Resolver) Reset() { *m = Resolver{} } -func (m *Resolver) String() string { return proto.CompactTextString(m) } -func (*Resolver) ProtoMessage() {} -func (*Resolver) Descriptor() ([]byte, []int) { return fileDescriptorResolverMap, []int{2} } - -type isResolver_Resolver interface { - isResolver_Resolver() - Equal(interface{}) bool -} - -type Resolver_GlooResolver struct { - GlooResolver *GlooResolver `protobuf:"bytes,1,opt,name=gloo_resolver,json=glooResolver,oneof"` -} -type Resolver_TemplateResolver struct { - TemplateResolver *TemplateResolver `protobuf:"bytes,2,opt,name=template_resolver,json=templateResolver,oneof"` -} -type Resolver_NodejsResolver struct { - NodejsResolver *NodeJSResolver `protobuf:"bytes,3,opt,name=nodejs_resolver,json=nodejsResolver,oneof"` -} - -func (*Resolver_GlooResolver) isResolver_Resolver() {} -func (*Resolver_TemplateResolver) isResolver_Resolver() {} -func (*Resolver_NodejsResolver) isResolver_Resolver() {} - -func (m *Resolver) GetResolver() isResolver_Resolver { - if m != nil { - return m.Resolver - } - return nil -} - -func (m *Resolver) GetGlooResolver() *GlooResolver { - if x, ok := m.GetResolver().(*Resolver_GlooResolver); ok { - return x.GlooResolver - } - return nil -} - -func (m *Resolver) GetTemplateResolver() *TemplateResolver { - if x, ok := m.GetResolver().(*Resolver_TemplateResolver); ok { - return x.TemplateResolver - } - return nil -} - -func (m *Resolver) GetNodejsResolver() *NodeJSResolver { - if x, ok := m.GetResolver().(*Resolver_NodejsResolver); ok { - return x.NodejsResolver - } - return nil -} - -// XXX_OneofFuncs is for the internal use of the proto package. -func (*Resolver) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { - return _Resolver_OneofMarshaler, _Resolver_OneofUnmarshaler, _Resolver_OneofSizer, []interface{}{ - (*Resolver_GlooResolver)(nil), - (*Resolver_TemplateResolver)(nil), - (*Resolver_NodejsResolver)(nil), - } -} - -func _Resolver_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { - m := msg.(*Resolver) - // resolver - switch x := m.Resolver.(type) { - case *Resolver_GlooResolver: - _ = b.EncodeVarint(1<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.GlooResolver); err != nil { - return err - } - case *Resolver_TemplateResolver: - _ = b.EncodeVarint(2<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.TemplateResolver); err != nil { - return err - } - case *Resolver_NodejsResolver: - _ = b.EncodeVarint(3<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.NodejsResolver); err != nil { - return err - } - case nil: - default: - return fmt.Errorf("Resolver.Resolver has unexpected type %T", x) - } - return nil -} - -func _Resolver_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { - m := msg.(*Resolver) - switch tag { - case 1: // resolver.gloo_resolver - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(GlooResolver) - err := b.DecodeMessage(msg) - m.Resolver = &Resolver_GlooResolver{msg} - return true, err - case 2: // resolver.template_resolver - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(TemplateResolver) - err := b.DecodeMessage(msg) - m.Resolver = &Resolver_TemplateResolver{msg} - return true, err - case 3: // resolver.nodejs_resolver - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(NodeJSResolver) - err := b.DecodeMessage(msg) - m.Resolver = &Resolver_NodejsResolver{msg} - return true, err - default: - return false, nil - } -} - -func _Resolver_OneofSizer(msg proto.Message) (n int) { - m := msg.(*Resolver) - // resolver - switch x := m.Resolver.(type) { - case *Resolver_GlooResolver: - s := proto.Size(x.GlooResolver) - n += proto.SizeVarint(1<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *Resolver_TemplateResolver: - s := proto.Size(x.TemplateResolver) - n += proto.SizeVarint(2<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *Resolver_NodejsResolver: - s := proto.Size(x.NodejsResolver) - n += proto.SizeVarint(3<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case nil: - default: - panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) - } - return n -} - -// GlooResolvers are the "meat" of Sqoop. GlooResolvers tell Sqoop how to invoke a "Gloo Function" -type GlooResolver struct { - // the Request Template, if specified, will become the body of the HTTP request used to invoke a function through Gloo - // input parameters, if needed, should be specified in the request template. See Sqoop's [Resolver documentation](TODO) - // for more information on Request Templates. - RequestTemplate string `protobuf:"bytes,1,opt,name=request_template,json=requestTemplate,proto3" json:"request_template,omitempty"` - // The response template, if specified, will transform the body of HTTP responses returned by Gloo functions. - // This field should be used if the object returned by the Gloo Function does not match the type specified in the GraphQL schema. - // It can also be used to modify or transform responses from their original state. See Sqoop's [Resolver documentation](TODO) - // for more information on Response Templates. - ResponseTemplate string `protobuf:"bytes,2,opt,name=response_template,json=responseTemplate,proto3" json:"response_template,omitempty"` - // Optional. Use to set the outbound HTTP Request header `Content-Type`. Defaults to `application/json` - ContentType string `protobuf:"bytes,3,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"` - // Specify the function the resolver should invoke. The function must be one registered to Gloo. - // - // Types that are valid to be assigned to Function: - // *GlooResolver_SingleFunction - // *GlooResolver_MultiFunction - Function isGlooResolver_Function `protobuf_oneof:"function"` -} - -func (m *GlooResolver) Reset() { *m = GlooResolver{} } -func (m *GlooResolver) String() string { return proto.CompactTextString(m) } -func (*GlooResolver) ProtoMessage() {} -func (*GlooResolver) Descriptor() ([]byte, []int) { return fileDescriptorResolverMap, []int{3} } - -type isGlooResolver_Function interface { - isGlooResolver_Function() - Equal(interface{}) bool -} - -type GlooResolver_SingleFunction struct { - SingleFunction *Function `protobuf:"bytes,4,opt,name=single_function,json=singleFunction,oneof"` -} -type GlooResolver_MultiFunction struct { - MultiFunction *MultiFunction `protobuf:"bytes,5,opt,name=multi_function,json=multiFunction,oneof"` -} - -func (*GlooResolver_SingleFunction) isGlooResolver_Function() {} -func (*GlooResolver_MultiFunction) isGlooResolver_Function() {} - -func (m *GlooResolver) GetFunction() isGlooResolver_Function { - if m != nil { - return m.Function - } - return nil -} - -func (m *GlooResolver) GetRequestTemplate() string { - if m != nil { - return m.RequestTemplate - } - return "" -} - -func (m *GlooResolver) GetResponseTemplate() string { - if m != nil { - return m.ResponseTemplate - } - return "" -} - -func (m *GlooResolver) GetContentType() string { - if m != nil { - return m.ContentType - } - return "" -} - -func (m *GlooResolver) GetSingleFunction() *Function { - if x, ok := m.GetFunction().(*GlooResolver_SingleFunction); ok { - return x.SingleFunction - } - return nil -} - -func (m *GlooResolver) GetMultiFunction() *MultiFunction { - if x, ok := m.GetFunction().(*GlooResolver_MultiFunction); ok { - return x.MultiFunction - } - return nil -} - -// XXX_OneofFuncs is for the internal use of the proto package. -func (*GlooResolver) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { - return _GlooResolver_OneofMarshaler, _GlooResolver_OneofUnmarshaler, _GlooResolver_OneofSizer, []interface{}{ - (*GlooResolver_SingleFunction)(nil), - (*GlooResolver_MultiFunction)(nil), - } -} - -func _GlooResolver_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { - m := msg.(*GlooResolver) - // function - switch x := m.Function.(type) { - case *GlooResolver_SingleFunction: - _ = b.EncodeVarint(4<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.SingleFunction); err != nil { - return err - } - case *GlooResolver_MultiFunction: - _ = b.EncodeVarint(5<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.MultiFunction); err != nil { - return err - } - case nil: - default: - return fmt.Errorf("GlooResolver.Function has unexpected type %T", x) - } - return nil -} - -func _GlooResolver_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { - m := msg.(*GlooResolver) - switch tag { - case 4: // function.single_function - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Function) - err := b.DecodeMessage(msg) - m.Function = &GlooResolver_SingleFunction{msg} - return true, err - case 5: // function.multi_function - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(MultiFunction) - err := b.DecodeMessage(msg) - m.Function = &GlooResolver_MultiFunction{msg} - return true, err - default: - return false, nil - } -} - -func _GlooResolver_OneofSizer(msg proto.Message) (n int) { - m := msg.(*GlooResolver) - // function - switch x := m.Function.(type) { - case *GlooResolver_SingleFunction: - s := proto.Size(x.SingleFunction) - n += proto.SizeVarint(4<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *GlooResolver_MultiFunction: - s := proto.Size(x.MultiFunction) - n += proto.SizeVarint(5<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case nil: - default: - panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) - } - return n -} - -// A reference to a function known to Gloo -type Function struct { - // Name of the Gloo Upstream that provides this function - Upstream string `protobuf:"bytes,1,opt,name=upstream,proto3" json:"upstream,omitempty"` - // Name of the function itself. See Gloo documentation for more details on functions in Gloo - Function string `protobuf:"bytes,2,opt,name=function,proto3" json:"function,omitempty"` -} - -func (m *Function) Reset() { *m = Function{} } -func (m *Function) String() string { return proto.CompactTextString(m) } -func (*Function) ProtoMessage() {} -func (*Function) Descriptor() ([]byte, []int) { return fileDescriptorResolverMap, []int{4} } - -func (m *Function) GetUpstream() string { - if m != nil { - return m.Upstream - } - return "" -} - -func (m *Function) GetFunction() string { - if m != nil { - return m.Function - } - return "" -} - -// A reference to a list of functions known to Gloo -type MultiFunction struct { - // A list of functions with weights. Must have size >= 1 - WeightedFunctions []*WeightedFunction `protobuf:"bytes,1,rep,name=weighted_functions,json=weightedFunctions" json:"weighted_functions,omitempty"` -} - -func (m *MultiFunction) Reset() { *m = MultiFunction{} } -func (m *MultiFunction) String() string { return proto.CompactTextString(m) } -func (*MultiFunction) ProtoMessage() {} -func (*MultiFunction) Descriptor() ([]byte, []int) { return fileDescriptorResolverMap, []int{5} } - -func (m *MultiFunction) GetWeightedFunctions() []*WeightedFunction { - if m != nil { - return m.WeightedFunctions - } - return nil -} - -type WeightedFunction struct { - // the function to call - Function *Function `protobuf:"bytes,1,opt,name=function" json:"function,omitempty"` - // Invoking each functoion will be balanced by the ratio of the function's weight to the total weight on a resolver - Weight uint32 `protobuf:"varint,2,opt,name=weight,proto3" json:"weight,omitempty"` -} - -func (m *WeightedFunction) Reset() { *m = WeightedFunction{} } -func (m *WeightedFunction) String() string { return proto.CompactTextString(m) } -func (*WeightedFunction) ProtoMessage() {} -func (*WeightedFunction) Descriptor() ([]byte, []int) { return fileDescriptorResolverMap, []int{6} } - -func (m *WeightedFunction) GetFunction() *Function { - if m != nil { - return m.Function - } - return nil -} - -func (m *WeightedFunction) GetWeight() uint32 { - if m != nil { - return m.Weight - } - return 0 -} - -// A Go-template which will return data for a Resolver without making a function call. Template Resolvers can make use -// of Sqoop's builtin template functions as well as the data provided by the Params object to the resolver. -// Read more about Templates and Resolvers in Sqoop's [Resolver documentation](TODO). -type TemplateResolver struct { - // the Go template as an inline string - InlineTemplate string `protobuf:"bytes,1,opt,name=inline_template,json=inlineTemplate,proto3" json:"inline_template,omitempty"` -} - -func (m *TemplateResolver) Reset() { *m = TemplateResolver{} } -func (m *TemplateResolver) String() string { return proto.CompactTextString(m) } -func (*TemplateResolver) ProtoMessage() {} -func (*TemplateResolver) Descriptor() ([]byte, []int) { return fileDescriptorResolverMap, []int{7} } - -func (m *TemplateResolver) GetInlineTemplate() string { - if m != nil { - return m.InlineTemplate - } - return "" -} - -// NOTE: currently unsupported -type NodeJSResolver struct { - InlineCode string `protobuf:"bytes,1,opt,name=inline_code,json=inlineCode,proto3" json:"inline_code,omitempty"` -} - -func (m *NodeJSResolver) Reset() { *m = NodeJSResolver{} } -func (m *NodeJSResolver) String() string { return proto.CompactTextString(m) } -func (*NodeJSResolver) ProtoMessage() {} -func (*NodeJSResolver) Descriptor() ([]byte, []int) { return fileDescriptorResolverMap, []int{8} } - -func (m *NodeJSResolver) GetInlineCode() string { - if m != nil { - return m.InlineCode - } - return "" -} - -func init() { - proto.RegisterType((*ResolverMap)(nil), "sqoop.api.v1.ResolverMap") - proto.RegisterType((*TypeResolver)(nil), "sqoop.api.v1.TypeResolver") - proto.RegisterType((*Resolver)(nil), "sqoop.api.v1.Resolver") - proto.RegisterType((*GlooResolver)(nil), "sqoop.api.v1.GlooResolver") - proto.RegisterType((*Function)(nil), "sqoop.api.v1.Function") - proto.RegisterType((*MultiFunction)(nil), "sqoop.api.v1.MultiFunction") - proto.RegisterType((*WeightedFunction)(nil), "sqoop.api.v1.WeightedFunction") - proto.RegisterType((*TemplateResolver)(nil), "sqoop.api.v1.TemplateResolver") - proto.RegisterType((*NodeJSResolver)(nil), "sqoop.api.v1.NodeJSResolver") -} -func (this *ResolverMap) Equal(that interface{}) bool { - if that == nil { - return this == nil - } - - that1, ok := that.(*ResolverMap) - if !ok { - that2, ok := that.(ResolverMap) - if ok { - that1 = &that2 - } else { - return false - } - } - if that1 == nil { - return this == nil - } else if this == nil { - return false - } - if this.Name != that1.Name { - return false - } - if len(this.Types) != len(that1.Types) { - return false - } - for i := range this.Types { - if !this.Types[i].Equal(that1.Types[i]) { - return false - } - } - if !this.Status.Equal(that1.Status) { - return false - } - if !this.Metadata.Equal(that1.Metadata) { - return false - } - return true -} -func (this *TypeResolver) Equal(that interface{}) bool { - if that == nil { - return this == nil - } - - that1, ok := that.(*TypeResolver) - if !ok { - that2, ok := that.(TypeResolver) - if ok { - that1 = &that2 - } else { - return false - } - } - if that1 == nil { - return this == nil - } else if this == nil { - return false - } - if len(this.Fields) != len(that1.Fields) { - return false - } - for i := range this.Fields { - if !this.Fields[i].Equal(that1.Fields[i]) { - return false - } - } - return true -} -func (this *Resolver) Equal(that interface{}) bool { - if that == nil { - return this == nil - } - - that1, ok := that.(*Resolver) - if !ok { - that2, ok := that.(Resolver) - if ok { - that1 = &that2 - } else { - return false - } - } - if that1 == nil { - return this == nil - } else if this == nil { - return false - } - if that1.Resolver == nil { - if this.Resolver != nil { - return false - } - } else if this.Resolver == nil { - return false - } else if !this.Resolver.Equal(that1.Resolver) { - return false - } - return true -} -func (this *Resolver_GlooResolver) Equal(that interface{}) bool { - if that == nil { - return this == nil - } - - that1, ok := that.(*Resolver_GlooResolver) - if !ok { - that2, ok := that.(Resolver_GlooResolver) - if ok { - that1 = &that2 - } else { - return false - } - } - if that1 == nil { - return this == nil - } else if this == nil { - return false - } - if !this.GlooResolver.Equal(that1.GlooResolver) { - return false - } - return true -} -func (this *Resolver_TemplateResolver) Equal(that interface{}) bool { - if that == nil { - return this == nil - } - - that1, ok := that.(*Resolver_TemplateResolver) - if !ok { - that2, ok := that.(Resolver_TemplateResolver) - if ok { - that1 = &that2 - } else { - return false - } - } - if that1 == nil { - return this == nil - } else if this == nil { - return false - } - if !this.TemplateResolver.Equal(that1.TemplateResolver) { - return false - } - return true -} -func (this *Resolver_NodejsResolver) Equal(that interface{}) bool { - if that == nil { - return this == nil - } - - that1, ok := that.(*Resolver_NodejsResolver) - if !ok { - that2, ok := that.(Resolver_NodejsResolver) - if ok { - that1 = &that2 - } else { - return false - } - } - if that1 == nil { - return this == nil - } else if this == nil { - return false - } - if !this.NodejsResolver.Equal(that1.NodejsResolver) { - return false - } - return true -} -func (this *GlooResolver) Equal(that interface{}) bool { - if that == nil { - return this == nil - } - - that1, ok := that.(*GlooResolver) - if !ok { - that2, ok := that.(GlooResolver) - if ok { - that1 = &that2 - } else { - return false - } - } - if that1 == nil { - return this == nil - } else if this == nil { - return false - } - if this.RequestTemplate != that1.RequestTemplate { - return false - } - if this.ResponseTemplate != that1.ResponseTemplate { - return false - } - if this.ContentType != that1.ContentType { - return false - } - if that1.Function == nil { - if this.Function != nil { - return false - } - } else if this.Function == nil { - return false - } else if !this.Function.Equal(that1.Function) { - return false - } - return true -} -func (this *GlooResolver_SingleFunction) Equal(that interface{}) bool { - if that == nil { - return this == nil - } - - that1, ok := that.(*GlooResolver_SingleFunction) - if !ok { - that2, ok := that.(GlooResolver_SingleFunction) - if ok { - that1 = &that2 - } else { - return false - } - } - if that1 == nil { - return this == nil - } else if this == nil { - return false - } - if !this.SingleFunction.Equal(that1.SingleFunction) { - return false - } - return true -} -func (this *GlooResolver_MultiFunction) Equal(that interface{}) bool { - if that == nil { - return this == nil - } - - that1, ok := that.(*GlooResolver_MultiFunction) - if !ok { - that2, ok := that.(GlooResolver_MultiFunction) - if ok { - that1 = &that2 - } else { - return false - } - } - if that1 == nil { - return this == nil - } else if this == nil { - return false - } - if !this.MultiFunction.Equal(that1.MultiFunction) { - return false - } - return true -} -func (this *Function) Equal(that interface{}) bool { - if that == nil { - return this == nil - } - - that1, ok := that.(*Function) - if !ok { - that2, ok := that.(Function) - if ok { - that1 = &that2 - } else { - return false - } - } - if that1 == nil { - return this == nil - } else if this == nil { - return false - } - if this.Upstream != that1.Upstream { - return false - } - if this.Function != that1.Function { - return false - } - return true -} -func (this *MultiFunction) Equal(that interface{}) bool { - if that == nil { - return this == nil - } - - that1, ok := that.(*MultiFunction) - if !ok { - that2, ok := that.(MultiFunction) - if ok { - that1 = &that2 - } else { - return false - } - } - if that1 == nil { - return this == nil - } else if this == nil { - return false - } - if len(this.WeightedFunctions) != len(that1.WeightedFunctions) { - return false - } - for i := range this.WeightedFunctions { - if !this.WeightedFunctions[i].Equal(that1.WeightedFunctions[i]) { - return false - } - } - return true -} -func (this *WeightedFunction) Equal(that interface{}) bool { - if that == nil { - return this == nil - } - - that1, ok := that.(*WeightedFunction) - if !ok { - that2, ok := that.(WeightedFunction) - if ok { - that1 = &that2 - } else { - return false - } - } - if that1 == nil { - return this == nil - } else if this == nil { - return false - } - if !this.Function.Equal(that1.Function) { - return false - } - if this.Weight != that1.Weight { - return false - } - return true -} -func (this *TemplateResolver) Equal(that interface{}) bool { - if that == nil { - return this == nil - } - - that1, ok := that.(*TemplateResolver) - if !ok { - that2, ok := that.(TemplateResolver) - if ok { - that1 = &that2 - } else { - return false - } - } - if that1 == nil { - return this == nil - } else if this == nil { - return false - } - if this.InlineTemplate != that1.InlineTemplate { - return false - } - return true -} -func (this *NodeJSResolver) Equal(that interface{}) bool { - if that == nil { - return this == nil - } - - that1, ok := that.(*NodeJSResolver) - if !ok { - that2, ok := that.(NodeJSResolver) - if ok { - that1 = &that2 - } else { - return false - } - } - if that1 == nil { - return this == nil - } else if this == nil { - return false - } - if this.InlineCode != that1.InlineCode { - return false - } - return true -} - -func init() { proto.RegisterFile("resolver_map.proto", fileDescriptorResolverMap) } - -var fileDescriptorResolverMap = []byte{ - // 691 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x54, 0x5d, 0x6b, 0xd4, 0x40, - 0x14, 0x35, 0xbb, 0xed, 0xb2, 0xbd, 0xd9, 0xcf, 0xd1, 0xca, 0xba, 0xa2, 0xad, 0x11, 0x71, 0xa5, - 0x34, 0x61, 0xeb, 0x8b, 0x56, 0x84, 0xb2, 0xc5, 0x2a, 0xd2, 0x8a, 0xa4, 0x82, 0x20, 0x94, 0x25, - 0xdd, 0xcc, 0xa6, 0xb1, 0x49, 0x26, 0xcd, 0x4c, 0xb6, 0xf4, 0xbf, 0x08, 0xbe, 0xf6, 0x57, 0xf9, - 0xe0, 0x0f, 0xf0, 0xc1, 0x5f, 0x20, 0x99, 0x8f, 0x7c, 0x2c, 0x55, 0xdf, 0x32, 0xe7, 0x9e, 0x73, - 0x72, 0xee, 0xcd, 0xcd, 0x00, 0x4a, 0x30, 0x25, 0xc1, 0x02, 0x27, 0xd3, 0xd0, 0x89, 0xcd, 0x38, - 0x21, 0x8c, 0x20, 0xfd, 0x22, 0x20, 0xc4, 0x74, 0x62, 0xdf, 0x5c, 0x8c, 0x87, 0x77, 0x3c, 0xe2, - 0x11, 0x8e, 0x5b, 0xd9, 0x93, 0xa0, 0x0c, 0xb7, 0x3c, 0x9f, 0x9d, 0xa5, 0xa7, 0xe6, 0x8c, 0x84, - 0x16, 0x25, 0x01, 0xd9, 0xf6, 0x89, 0xe5, 0x05, 0x84, 0x58, 0x4e, 0xec, 0x5b, 0x8b, 0xb1, 0x45, - 0x99, 0xc3, 0x52, 0x2a, 0xc9, 0xdb, 0xff, 0x21, 0x87, 0x98, 0x39, 0xae, 0xc3, 0x1c, 0x41, 0x37, - 0xae, 0x6b, 0xa0, 0xdb, 0x32, 0xd5, 0x91, 0x13, 0x23, 0x04, 0x2b, 0x91, 0x13, 0xe2, 0x81, 0xb6, - 0xa9, 0x8d, 0xd6, 0x6c, 0xfe, 0x8c, 0x5e, 0xc2, 0x2a, 0xbb, 0x8a, 0x31, 0x1d, 0xd4, 0x37, 0xeb, - 0x23, 0x7d, 0xe7, 0xb1, 0x59, 0x8a, 0x6c, 0x96, 0xc4, 0xe6, 0xa7, 0x8c, 0xf5, 0x26, 0x62, 0xc9, - 0x95, 0x2d, 0x14, 0x68, 0x02, 0x0d, 0x91, 0x6e, 0xb0, 0xb2, 0xa9, 0x8d, 0xf4, 0x9d, 0xdb, 0xa6, - 0x57, 0xd2, 0x1e, 0xf3, 0xd2, 0x64, 0xfd, 0xf7, 0x8f, 0x8d, 0x3e, 0xc3, 0x94, 0xb9, 0xfe, 0x7c, - 0xbe, 0x6b, 0xf8, 0x5e, 0x44, 0x12, 0x6c, 0xd8, 0x52, 0x89, 0xc6, 0xd0, 0x54, 0xa1, 0x07, 0xab, - 0xdc, 0x65, 0xbd, 0xe2, 0x72, 0x24, 0x8b, 0x76, 0x4e, 0x1b, 0x1e, 0x03, 0x14, 0x59, 0x50, 0x0f, - 0xea, 0xe7, 0xf8, 0x4a, 0xb6, 0x94, 0x3d, 0x22, 0x0b, 0x56, 0x17, 0x4e, 0x90, 0xe2, 0x41, 0x8d, - 0xfb, 0xdd, 0xab, 0x74, 0x94, 0x29, 0x55, 0x57, 0xb6, 0xe0, 0xed, 0xd6, 0x5e, 0x68, 0xc6, 0x77, - 0x0d, 0x5a, 0xe5, 0x1a, 0x7a, 0x0d, 0x8d, 0xb9, 0x8f, 0x03, 0x97, 0x0e, 0x34, 0x3e, 0x98, 0x27, - 0x7f, 0xb5, 0x31, 0x0f, 0x38, 0x4f, 0x8c, 0x46, 0x8a, 0x86, 0x1f, 0x41, 0x2f, 0xc1, 0x37, 0xa4, - 0xdc, 0xaa, 0xa6, 0x5c, 0xbf, 0x71, 0xee, 0xe5, 0x84, 0xbf, 0x34, 0x68, 0xe6, 0xe9, 0xf6, 0xa0, - 0x9d, 0x4d, 0x69, 0xaa, 0x76, 0x8e, 0x3b, 0x2f, 0xf7, 0xfa, 0x36, 0x20, 0x44, 0x29, 0xde, 0xdd, - 0xb2, 0x5b, 0x5e, 0xe9, 0x8c, 0x0e, 0xa1, 0xcf, 0x70, 0x18, 0x07, 0x0e, 0xc3, 0x85, 0x8b, 0xc8, - 0xf2, 0xa0, 0xda, 0xaa, 0x64, 0x95, 0x9c, 0x7a, 0x6c, 0x09, 0x43, 0x07, 0xd0, 0x8d, 0x88, 0x8b, - 0xbf, 0xd2, 0xc2, 0xab, 0xce, 0xbd, 0xee, 0x57, 0xbc, 0x3e, 0x10, 0x17, 0xbf, 0x3f, 0x2e, 0x39, - 0x75, 0x84, 0x4a, 0x21, 0x13, 0x80, 0xa6, 0x32, 0x30, 0xbe, 0xd5, 0xa0, 0x55, 0x6e, 0x01, 0x3d, - 0x83, 0x5e, 0x82, 0x2f, 0x52, 0x4c, 0xd9, 0x54, 0x05, 0x90, 0x13, 0xed, 0x4a, 0x5c, 0x65, 0x45, - 0x5b, 0xd0, 0x4f, 0x30, 0x8d, 0x49, 0x44, 0x71, 0xc1, 0xad, 0x71, 0x6e, 0x4f, 0x15, 0x72, 0xf2, - 0x23, 0x68, 0xcd, 0x48, 0xc4, 0x70, 0xc4, 0xa6, 0xd9, 0x62, 0xf3, 0xe4, 0x6b, 0xb6, 0x2e, 0xb1, - 0xec, 0x53, 0xa3, 0x3d, 0xe8, 0x52, 0x3f, 0xf2, 0x02, 0x3c, 0x9d, 0xa7, 0xd1, 0x8c, 0xf9, 0x24, - 0x92, 0x3b, 0x5f, 0xfd, 0x6e, 0x07, 0xb2, 0x98, 0x75, 0x26, 0xf8, 0x0a, 0x41, 0xfb, 0xd0, 0x09, - 0xd3, 0x80, 0xf9, 0x85, 0x81, 0x58, 0xf7, 0x61, 0xc5, 0xe0, 0x28, 0xa3, 0x94, 0x5c, 0xda, 0x61, - 0x19, 0xc8, 0xc6, 0xa3, 0xe4, 0xc6, 0x04, 0x9a, 0xb9, 0xf9, 0x10, 0x9a, 0x69, 0x4c, 0x59, 0x82, - 0x9d, 0x50, 0x4e, 0x24, 0x3f, 0x67, 0xb5, 0xfc, 0x95, 0x62, 0x02, 0x85, 0xc7, 0x09, 0xb4, 0x2b, - 0x6f, 0x44, 0x87, 0x80, 0x2e, 0xb1, 0xef, 0x9d, 0x31, 0xec, 0xe6, 0x41, 0xd5, 0x1f, 0x50, 0x5d, - 0x8b, 0xcf, 0x92, 0xa6, 0xa4, 0x76, 0xff, 0x72, 0x09, 0xa1, 0xc6, 0x09, 0xf4, 0x96, 0x69, 0xd9, - 0x0f, 0x9f, 0xc7, 0xd1, 0xfe, 0x31, 0xc2, 0x22, 0x25, 0xba, 0x0b, 0x0d, 0xe1, 0xcd, 0xf3, 0xb7, - 0x6d, 0x79, 0x32, 0x5e, 0x41, 0x6f, 0x79, 0x39, 0xd1, 0x53, 0xe8, 0xfa, 0x51, 0xe0, 0x47, 0x78, - 0x79, 0x45, 0x3a, 0x02, 0x56, 0x02, 0x63, 0x0c, 0x9d, 0xea, 0x36, 0xa2, 0x0d, 0xd0, 0xa5, 0x74, - 0x46, 0x5c, 0x25, 0x03, 0x01, 0xed, 0x13, 0x17, 0x4f, 0xcc, 0xeb, 0x9f, 0x0f, 0xb5, 0x2f, 0xa3, - 0x1b, 0xee, 0xe0, 0xac, 0x01, 0x2b, 0x3e, 0xf7, 0xf8, 0x3d, 0xcc, 0x2f, 0x47, 0x6b, 0x31, 0x3e, - 0x6d, 0xf0, 0x5b, 0xf8, 0xf9, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xee, 0x0c, 0x12, 0x24, 0x1a, - 0x06, 0x00, 0x00, -} diff --git a/pkg/api/types/v1/schema.pb.go b/pkg/api/types/v1/schema.pb.go deleted file mode 100644 index f36b6a7..0000000 --- a/pkg/api/types/v1/schema.pb.go +++ /dev/null @@ -1,139 +0,0 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: schema.proto - -package v1 - -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import _ "github.com/gogo/protobuf/gogoproto" -import gloo_api_v1 "github.com/solo-io/gloo/pkg/api/types/v1" -import gloo_api_v11 "github.com/solo-io/gloo/pkg/api/types/v1" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// -// The Schema object wraps the user's GraphQL Schema, which is stored as an inline string. -// The Schema Object contains a Status field which is used by Sqoop to validate the user's input schema. -type Schema struct { - // Schema Names must be unique and follow the following syntax rules: - // One or more lowercase rfc1035/rfc1123 labels separated by '.' with a maximum length of 253 characters. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // name of the resolver map to use to resolve this schema. - // if the user leaves this empty, Sqoop will generate the skeleton of a resolver map for the user - ResolverMap string `protobuf:"bytes,2,opt,name=resolver_map,json=resolverMap,proto3" json:"resolver_map,omitempty"` - // inline the entire graphql schema as a string here - InlineSchema string `protobuf:"bytes,3,opt,name=inline_schema,json=inlineSchema,proto3" json:"inline_schema,omitempty"` - // Status indicates the validation status of the role resource. - // Status is read-only by clients, and set by gloo during validation - Status *gloo_api_v1.Status `protobuf:"bytes,6,opt,name=status" json:"status,omitempty" testdiff:"ignore"` - // Metadata contains the resource metadata for the role - Metadata *gloo_api_v11.Metadata `protobuf:"bytes,7,opt,name=metadata" json:"metadata,omitempty"` -} - -func (m *Schema) Reset() { *m = Schema{} } -func (m *Schema) String() string { return proto.CompactTextString(m) } -func (*Schema) ProtoMessage() {} -func (*Schema) Descriptor() ([]byte, []int) { return fileDescriptorSchema, []int{0} } - -func (m *Schema) GetName() string { - if m != nil { - return m.Name - } - return "" -} - -func (m *Schema) GetResolverMap() string { - if m != nil { - return m.ResolverMap - } - return "" -} - -func (m *Schema) GetInlineSchema() string { - if m != nil { - return m.InlineSchema - } - return "" -} - -func (m *Schema) GetStatus() *gloo_api_v1.Status { - if m != nil { - return m.Status - } - return nil -} - -func (m *Schema) GetMetadata() *gloo_api_v11.Metadata { - if m != nil { - return m.Metadata - } - return nil -} - -func init() { - proto.RegisterType((*Schema)(nil), "sqoop.api.v1.Schema") -} -func (this *Schema) Equal(that interface{}) bool { - if that == nil { - return this == nil - } - - that1, ok := that.(*Schema) - if !ok { - that2, ok := that.(Schema) - if ok { - that1 = &that2 - } else { - return false - } - } - if that1 == nil { - return this == nil - } else if this == nil { - return false - } - if this.Name != that1.Name { - return false - } - if this.ResolverMap != that1.ResolverMap { - return false - } - if this.InlineSchema != that1.InlineSchema { - return false - } - if !this.Status.Equal(that1.Status) { - return false - } - if !this.Metadata.Equal(that1.Metadata) { - return false - } - return true -} - -func init() { proto.RegisterFile("schema.proto", fileDescriptorSchema) } - -var fileDescriptorSchema = []byte{ - // 284 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x90, 0xb1, 0x4e, 0xc3, 0x30, - 0x14, 0x45, 0x15, 0x40, 0x01, 0x9c, 0x30, 0x60, 0xa8, 0x14, 0x75, 0x80, 0x12, 0x96, 0x48, 0xa8, - 0xb6, 0x02, 0x1b, 0x63, 0xf6, 0x2e, 0xe9, 0xc6, 0x52, 0xb9, 0xad, 0xeb, 0x5a, 0xd8, 0x79, 0x26, - 0x76, 0x23, 0xf1, 0x47, 0xfc, 0x13, 0x12, 0x03, 0x9f, 0xc0, 0x17, 0xa0, 0x3a, 0x2e, 0x08, 0x09, - 0xa9, 0xdb, 0xd3, 0xbb, 0xc7, 0xd7, 0xf7, 0x5d, 0x94, 0xda, 0xc5, 0x9a, 0x6b, 0x46, 0x4c, 0x0b, - 0x0e, 0x70, 0xf2, 0xa2, 0x00, 0x08, 0x33, 0x92, 0x74, 0xe5, 0xf0, 0x52, 0x80, 0x00, 0xbf, 0xa7, - 0xdb, 0xa9, 0x47, 0x86, 0x77, 0x42, 0xba, 0xf5, 0x66, 0x4e, 0x16, 0xa0, 0xa9, 0x05, 0x05, 0x63, - 0x09, 0x54, 0x28, 0x00, 0xca, 0x8c, 0xa4, 0x5d, 0x49, 0xad, 0x63, 0x6e, 0x63, 0x03, 0x3c, 0xde, - 0x03, 0x6b, 0xee, 0xd8, 0x92, 0xb9, 0xf0, 0x7d, 0xfe, 0x1e, 0xa1, 0x78, 0xea, 0xf3, 0x60, 0x8c, - 0x8e, 0x1a, 0xa6, 0x79, 0x16, 0x8d, 0xa2, 0xe2, 0xb4, 0xf6, 0x33, 0xbe, 0x41, 0x69, 0xcb, 0x2d, - 0xa8, 0x8e, 0xb7, 0x33, 0xcd, 0x4c, 0x76, 0xe0, 0xb5, 0x64, 0xb7, 0x9b, 0x30, 0x83, 0x6f, 0xd1, - 0x99, 0x6c, 0x94, 0x6c, 0xf8, 0xac, 0xbf, 0x2b, 0x3b, 0xf4, 0x4c, 0xda, 0x2f, 0x83, 0x77, 0x85, - 0xe2, 0x3e, 0x65, 0x16, 0x8f, 0xa2, 0x22, 0xb9, 0xbf, 0x20, 0xe2, 0xf7, 0x6c, 0x32, 0xf5, 0x52, - 0x35, 0xf8, 0xfa, 0xb8, 0x3e, 0x77, 0xdc, 0xba, 0xa5, 0x5c, 0xad, 0x1e, 0x73, 0x29, 0x1a, 0x68, - 0x79, 0x5e, 0x87, 0x97, 0xb8, 0x44, 0x27, 0xbb, 0xf0, 0xd9, 0xb1, 0x77, 0x19, 0xfc, 0x71, 0x99, - 0x04, 0xb1, 0xfe, 0xc1, 0x2a, 0xf2, 0xf6, 0x79, 0x15, 0x3d, 0x15, 0xff, 0x54, 0xb2, 0x6d, 0x9d, - 0x9a, 0x67, 0xe1, 0x6b, 0x71, 0xaf, 0x86, 0x5b, 0xda, 0x95, 0xf3, 0xd8, 0x97, 0xf2, 0xf0, 0x1d, - 0x00, 0x00, 0xff, 0xff, 0x71, 0xae, 0x89, 0xbe, 0xa3, 0x01, 0x00, 0x00, -} diff --git a/pkg/api/v1/api_event_loop.sk.go b/pkg/api/v1/api_event_loop.sk.go new file mode 100644 index 0000000..f0f2c84 --- /dev/null +++ b/pkg/api/v1/api_event_loop.sk.go @@ -0,0 +1,96 @@ +// Code generated by solo-kit. DO NOT EDIT. + +package v1 + +import ( + "context" + + "go.opencensus.io/trace" + + "github.com/hashicorp/go-multierror" + + "github.com/solo-io/solo-kit/pkg/api/v1/clients" + "github.com/solo-io/solo-kit/pkg/errors" + "github.com/solo-io/solo-kit/pkg/utils/contextutils" + "github.com/solo-io/solo-kit/pkg/utils/errutils" +) + +type ApiSyncer interface { + Sync(context.Context, *ApiSnapshot) error +} + +type ApiSyncers []ApiSyncer + +func (s ApiSyncers) Sync(ctx context.Context, snapshot *ApiSnapshot) error { + var multiErr *multierror.Error + for _, syncer := range s { + if err := syncer.Sync(ctx, snapshot); err != nil { + multiErr = multierror.Append(multiErr, err) + } + } + return multiErr.ErrorOrNil() +} + +type ApiEventLoop interface { + Run(namespaces []string, opts clients.WatchOpts) (<-chan error, error) +} + +type apiEventLoop struct { + emitter ApiEmitter + syncer ApiSyncer +} + +func NewApiEventLoop(emitter ApiEmitter, syncer ApiSyncer) ApiEventLoop { + return &apiEventLoop{ + emitter: emitter, + syncer: syncer, + } +} + +func (el *apiEventLoop) Run(namespaces []string, opts clients.WatchOpts) (<-chan error, error) { + opts = opts.WithDefaults() + opts.Ctx = contextutils.WithLogger(opts.Ctx, "v1.event_loop") + logger := contextutils.LoggerFrom(opts.Ctx) + logger.Infof("event loop started") + + errs := make(chan error) + + watch, emitterErrs, err := el.emitter.Snapshots(namespaces, opts) + if err != nil { + return nil, errors.Wrapf(err, "starting snapshot watch") + } + go errutils.AggregateErrs(opts.Ctx, errs, emitterErrs, "v1.emitter errors") + go func() { + // create a new context for each loop, cancel it before each loop + var cancel context.CancelFunc = func() {} + // use closure to allow cancel function to be updated as context changes + defer func() { cancel() }() + for { + select { + case snapshot, ok := <-watch: + if !ok { + return + } + // cancel any open watches from previous loop + cancel() + + ctx, span := trace.StartSpan(opts.Ctx, "api.sqoop.solo.io.EventLoopSync") + ctx, canc := context.WithCancel(ctx) + cancel = canc + err := el.syncer.Sync(ctx, snapshot) + span.End() + + if err != nil { + select { + case errs <- err: + default: + logger.Errorf("write error channel is full! could not propagate err: %v", err) + } + } + case <-opts.Ctx.Done(): + return + } + } + }() + return errs, nil +} diff --git a/pkg/api/v1/api_event_loop_test.go b/pkg/api/v1/api_event_loop_test.go new file mode 100644 index 0000000..9ca3969 --- /dev/null +++ b/pkg/api/v1/api_event_loop_test.go @@ -0,0 +1,71 @@ +// Code generated by solo-kit. DO NOT EDIT. + +// +build solokit + +package v1 + +import ( + "context" + "sync" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/solo-io/solo-kit/pkg/api/v1/clients" + "github.com/solo-io/solo-kit/pkg/api/v1/clients/factory" + "github.com/solo-io/solo-kit/pkg/api/v1/clients/memory" +) + +var _ = Describe("ApiEventLoop", func() { + var ( + namespace string + emitter ApiEmitter + err error + ) + + BeforeEach(func() { + + resolverMapClientFactory := &factory.MemoryResourceClientFactory{ + Cache: memory.NewInMemoryResourceCache(), + } + resolverMapClient, err := NewResolverMapClient(resolverMapClientFactory) + Expect(err).NotTo(HaveOccurred()) + + schemaClientFactory := &factory.MemoryResourceClientFactory{ + Cache: memory.NewInMemoryResourceCache(), + } + schemaClient, err := NewSchemaClient(schemaClientFactory) + Expect(err).NotTo(HaveOccurred()) + + emitter = NewApiEmitter(resolverMapClient, schemaClient) + }) + It("runs sync function on a new snapshot", func() { + _, err = emitter.ResolverMap().Write(NewResolverMap(namespace, "jerry"), clients.WriteOpts{}) + Expect(err).NotTo(HaveOccurred()) + _, err = emitter.Schema().Write(NewSchema(namespace, "jerry"), clients.WriteOpts{}) + Expect(err).NotTo(HaveOccurred()) + sync := &mockApiSyncer{} + el := NewApiEventLoop(emitter, sync) + _, err := el.Run([]string{namespace}, clients.WatchOpts{}) + Expect(err).NotTo(HaveOccurred()) + Eventually(sync.Synced, 5*time.Second).Should(BeTrue()) + }) +}) + +type mockApiSyncer struct { + synced bool + mutex sync.Mutex +} + +func (s *mockApiSyncer) Synced() bool { + s.mutex.Lock() + defer s.mutex.Unlock() + return s.synced +} + +func (s *mockApiSyncer) Sync(ctx context.Context, snap *ApiSnapshot) error { + s.mutex.Lock() + s.synced = true + s.mutex.Unlock() + return nil +} diff --git a/pkg/api/v1/api_snapshot.sk.go b/pkg/api/v1/api_snapshot.sk.go new file mode 100644 index 0000000..d268dc4 --- /dev/null +++ b/pkg/api/v1/api_snapshot.sk.go @@ -0,0 +1,43 @@ +// Code generated by solo-kit. DO NOT EDIT. + +package v1 + +import ( + "github.com/solo-io/solo-kit/pkg/utils/hashutils" + "go.uber.org/zap" +) + +type ApiSnapshot struct { + ResolverMaps ResolverMapsByNamespace + Schemas SchemasByNamespace +} + +func (s ApiSnapshot) Clone() ApiSnapshot { + return ApiSnapshot{ + ResolverMaps: s.ResolverMaps.Clone(), + Schemas: s.Schemas.Clone(), + } +} + +func (s ApiSnapshot) Hash() uint64 { + return hashutils.HashAll( + s.hashResolverMaps(), + s.hashSchemas(), + ) +} + +func (s ApiSnapshot) hashResolverMaps() uint64 { + return hashutils.HashAll(s.ResolverMaps.List().AsInterfaces()...) +} + +func (s ApiSnapshot) hashSchemas() uint64 { + return hashutils.HashAll(s.Schemas.List().AsInterfaces()...) +} + +func (s ApiSnapshot) HashFields() []zap.Field { + var fields []zap.Field + fields = append(fields, zap.Uint64("resolverMaps", s.hashResolverMaps())) + fields = append(fields, zap.Uint64("schemas", s.hashSchemas())) + + return append(fields, zap.Uint64("snapshotHash", s.Hash())) +} diff --git a/pkg/api/v1/api_snapshot_emitter.sk.go b/pkg/api/v1/api_snapshot_emitter.sk.go new file mode 100644 index 0000000..07afa15 --- /dev/null +++ b/pkg/api/v1/api_snapshot_emitter.sk.go @@ -0,0 +1,209 @@ +// Code generated by solo-kit. DO NOT EDIT. + +package v1 + +import ( + "sync" + "time" + + "go.opencensus.io/stats" + "go.opencensus.io/stats/view" + "go.opencensus.io/tag" + + "github.com/solo-io/solo-kit/pkg/api/v1/clients" + "github.com/solo-io/solo-kit/pkg/errors" + "github.com/solo-io/solo-kit/pkg/utils/errutils" +) + +var ( + mApiSnapshotIn = stats.Int64("api.sqoop.solo.io/snap_emitter/snap_in", "The number of snapshots in", "1") + mApiSnapshotOut = stats.Int64("api.sqoop.solo.io/snap_emitter/snap_out", "The number of snapshots out", "1") + + apisnapshotInView = &view.View{ + Name: "api.sqoop.solo.io_snap_emitter/snap_in", + Measure: mApiSnapshotIn, + Description: "The number of snapshots updates coming in", + Aggregation: view.Count(), + TagKeys: []tag.Key{}, + } + apisnapshotOutView = &view.View{ + Name: "api.sqoop.solo.io/snap_emitter/snap_out", + Measure: mApiSnapshotOut, + Description: "The number of snapshots updates going out", + Aggregation: view.Count(), + TagKeys: []tag.Key{}, + } +) + +func init() { + view.Register(apisnapshotInView, apisnapshotOutView) +} + +type ApiEmitter interface { + Register() error + ResolverMap() ResolverMapClient + Schema() SchemaClient + Snapshots(watchNamespaces []string, opts clients.WatchOpts) (<-chan *ApiSnapshot, <-chan error, error) +} + +func NewApiEmitter(resolverMapClient ResolverMapClient, schemaClient SchemaClient) ApiEmitter { + return NewApiEmitterWithEmit(resolverMapClient, schemaClient, make(chan struct{})) +} + +func NewApiEmitterWithEmit(resolverMapClient ResolverMapClient, schemaClient SchemaClient, emit <-chan struct{}) ApiEmitter { + return &apiEmitter{ + resolverMap: resolverMapClient, + schema: schemaClient, + forceEmit: emit, + } +} + +type apiEmitter struct { + forceEmit <-chan struct{} + resolverMap ResolverMapClient + schema SchemaClient +} + +func (c *apiEmitter) Register() error { + if err := c.resolverMap.Register(); err != nil { + return err + } + if err := c.schema.Register(); err != nil { + return err + } + return nil +} + +func (c *apiEmitter) ResolverMap() ResolverMapClient { + return c.resolverMap +} + +func (c *apiEmitter) Schema() SchemaClient { + return c.schema +} + +func (c *apiEmitter) Snapshots(watchNamespaces []string, opts clients.WatchOpts) (<-chan *ApiSnapshot, <-chan error, error) { + + if len(watchNamespaces) == 0 { + watchNamespaces = []string{""} + } + + for _, ns := range watchNamespaces { + if ns == "" && len(watchNamespaces) > 1 { + return nil, nil, errors.Errorf("the \"\" namespace is used to watch all namespaces. Snapshots can either be tracked for " + + "specific namespaces or \"\" AllNamespaces, but not both.") + } + } + + errs := make(chan error) + var done sync.WaitGroup + ctx := opts.Ctx + /* Create channel for ResolverMap */ + type resolverMapListWithNamespace struct { + list ResolverMapList + namespace string + } + resolverMapChan := make(chan resolverMapListWithNamespace) + /* Create channel for Schema */ + type schemaListWithNamespace struct { + list SchemaList + namespace string + } + schemaChan := make(chan schemaListWithNamespace) + + for _, namespace := range watchNamespaces { + /* Setup namespaced watch for ResolverMap */ + resolverMapNamespacesChan, resolverMapErrs, err := c.resolverMap.Watch(namespace, opts) + if err != nil { + return nil, nil, errors.Wrapf(err, "starting ResolverMap watch") + } + + done.Add(1) + go func(namespace string) { + defer done.Done() + errutils.AggregateErrs(ctx, errs, resolverMapErrs, namespace+"-resolverMaps") + }(namespace) + /* Setup namespaced watch for Schema */ + schemaNamespacesChan, schemaErrs, err := c.schema.Watch(namespace, opts) + if err != nil { + return nil, nil, errors.Wrapf(err, "starting Schema watch") + } + + done.Add(1) + go func(namespace string) { + defer done.Done() + errutils.AggregateErrs(ctx, errs, schemaErrs, namespace+"-schemas") + }(namespace) + + /* Watch for changes and update snapshot */ + go func(namespace string) { + for { + select { + case <-ctx.Done(): + return + case resolverMapList := <-resolverMapNamespacesChan: + select { + case <-ctx.Done(): + return + case resolverMapChan <- resolverMapListWithNamespace{list: resolverMapList, namespace: namespace}: + } + case schemaList := <-schemaNamespacesChan: + select { + case <-ctx.Done(): + return + case schemaChan <- schemaListWithNamespace{list: schemaList, namespace: namespace}: + } + } + } + }(namespace) + } + + snapshots := make(chan *ApiSnapshot) + go func() { + originalSnapshot := ApiSnapshot{} + currentSnapshot := originalSnapshot.Clone() + timer := time.NewTicker(time.Second * 1) + sync := func() { + if originalSnapshot.Hash() == currentSnapshot.Hash() { + return + } + + stats.Record(ctx, mApiSnapshotOut.M(1)) + originalSnapshot = currentSnapshot.Clone() + sentSnapshot := currentSnapshot.Clone() + snapshots <- &sentSnapshot + } + + for { + record := func() { stats.Record(ctx, mApiSnapshotIn.M(1)) } + + select { + case <-timer.C: + sync() + case <-ctx.Done(): + close(snapshots) + done.Wait() + close(errs) + return + case <-c.forceEmit: + sentSnapshot := currentSnapshot.Clone() + snapshots <- &sentSnapshot + case resolverMapNamespacedList := <-resolverMapChan: + record() + + namespace := resolverMapNamespacedList.namespace + resolverMapList := resolverMapNamespacedList.list + + currentSnapshot.ResolverMaps[namespace] = resolverMapList + case schemaNamespacedList := <-schemaChan: + record() + + namespace := schemaNamespacedList.namespace + schemaList := schemaNamespacedList.list + + currentSnapshot.Schemas[namespace] = schemaList + } + } + }() + return snapshots, errs, nil +} diff --git a/pkg/api/v1/api_snapshot_emitter_test.go b/pkg/api/v1/api_snapshot_emitter_test.go new file mode 100644 index 0000000..e7aad23 --- /dev/null +++ b/pkg/api/v1/api_snapshot_emitter_test.go @@ -0,0 +1,343 @@ +// Code generated by solo-kit. DO NOT EDIT. + +// +build solokit + +package v1 + +import ( + "context" + "os" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/solo-io/go-utils/kubeutils" + "github.com/solo-io/solo-kit/pkg/api/v1/clients" + "github.com/solo-io/solo-kit/pkg/api/v1/clients/factory" + kuberc "github.com/solo-io/solo-kit/pkg/api/v1/clients/kube" + "github.com/solo-io/solo-kit/pkg/utils/log" + "github.com/solo-io/solo-kit/test/helpers" + "github.com/solo-io/solo-kit/test/setup" + "k8s.io/client-go/rest" + + // Needed to run tests in GKE + _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" + + // From https://github.com/kubernetes/client-go/blob/53c7adfd0294caa142d961e1f780f74081d5b15f/examples/out-of-cluster-client-configuration/main.go#L31 + _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" +) + +var _ = Describe("V1Emitter", func() { + if os.Getenv("RUN_KUBE_TESTS") != "1" { + log.Printf("This test creates kubernetes resources and is disabled by default. To enable, set RUN_KUBE_TESTS=1 in your env.") + return + } + var ( + namespace1 string + namespace2 string + name1, name2 = "angela" + helpers.RandString(3), "bob" + helpers.RandString(3) + cfg *rest.Config + emitter ApiEmitter + resolverMapClient ResolverMapClient + schemaClient SchemaClient + ) + + BeforeEach(func() { + namespace1 = helpers.RandString(8) + namespace2 = helpers.RandString(8) + var err error + cfg, err = kubeutils.GetConfig("", "") + Expect(err).NotTo(HaveOccurred()) + err = setup.SetupKubeForTest(namespace1) + Expect(err).NotTo(HaveOccurred()) + err = setup.SetupKubeForTest(namespace2) + Expect(err).NotTo(HaveOccurred()) + // ResolverMap Constructor + resolverMapClientFactory := &factory.KubeResourceClientFactory{ + Crd: ResolverMapCrd, + Cfg: cfg, + SharedCache: kuberc.NewKubeCache(context.TODO()), + } + resolverMapClient, err = NewResolverMapClient(resolverMapClientFactory) + Expect(err).NotTo(HaveOccurred()) + // Schema Constructor + schemaClientFactory := &factory.KubeResourceClientFactory{ + Crd: SchemaCrd, + Cfg: cfg, + SharedCache: kuberc.NewKubeCache(context.TODO()), + } + schemaClient, err = NewSchemaClient(schemaClientFactory) + Expect(err).NotTo(HaveOccurred()) + emitter = NewApiEmitter(resolverMapClient, schemaClient) + }) + AfterEach(func() { + setup.TeardownKube(namespace1) + setup.TeardownKube(namespace2) + }) + It("tracks snapshots on changes to any resource", func() { + ctx := context.Background() + err := emitter.Register() + Expect(err).NotTo(HaveOccurred()) + + snapshots, errs, err := emitter.Snapshots([]string{namespace1, namespace2}, clients.WatchOpts{ + Ctx: ctx, + RefreshRate: time.Second, + }) + Expect(err).NotTo(HaveOccurred()) + + var snap *ApiSnapshot + + /* + ResolverMap + */ + + assertSnapshotResolverMaps := func(expectResolverMaps ResolverMapList, unexpectResolverMaps ResolverMapList) { + drain: + for { + select { + case snap = <-snapshots: + for _, expected := range expectResolverMaps { + if _, err := snap.ResolverMaps.List().Find(expected.Metadata.Ref().Strings()); err != nil { + continue drain + } + } + for _, unexpected := range unexpectResolverMaps { + if _, err := snap.ResolverMaps.List().Find(unexpected.Metadata.Ref().Strings()); err == nil { + continue drain + } + } + break drain + case err := <-errs: + Expect(err).NotTo(HaveOccurred()) + case <-time.After(time.Second * 10): + nsList1, _ := resolverMapClient.List(namespace1, clients.ListOpts{}) + nsList2, _ := resolverMapClient.List(namespace2, clients.ListOpts{}) + combined := ResolverMapsByNamespace{ + namespace1: nsList1, + namespace2: nsList2, + } + Fail("expected final snapshot before 10 seconds. expected " + log.Sprintf("%v", combined)) + } + } + } + resolverMap1a, err := resolverMapClient.Write(NewResolverMap(namespace1, name1), clients.WriteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + resolverMap1b, err := resolverMapClient.Write(NewResolverMap(namespace2, name1), clients.WriteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + + assertSnapshotResolverMaps(ResolverMapList{resolverMap1a, resolverMap1b}, nil) + resolverMap2a, err := resolverMapClient.Write(NewResolverMap(namespace1, name2), clients.WriteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + resolverMap2b, err := resolverMapClient.Write(NewResolverMap(namespace2, name2), clients.WriteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + + assertSnapshotResolverMaps(ResolverMapList{resolverMap1a, resolverMap1b, resolverMap2a, resolverMap2b}, nil) + + err = resolverMapClient.Delete(resolverMap2a.Metadata.Namespace, resolverMap2a.Metadata.Name, clients.DeleteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + err = resolverMapClient.Delete(resolverMap2b.Metadata.Namespace, resolverMap2b.Metadata.Name, clients.DeleteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + + assertSnapshotResolverMaps(ResolverMapList{resolverMap1a, resolverMap1b}, ResolverMapList{resolverMap2a, resolverMap2b}) + + err = resolverMapClient.Delete(resolverMap1a.Metadata.Namespace, resolverMap1a.Metadata.Name, clients.DeleteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + err = resolverMapClient.Delete(resolverMap1b.Metadata.Namespace, resolverMap1b.Metadata.Name, clients.DeleteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + + assertSnapshotResolverMaps(nil, ResolverMapList{resolverMap1a, resolverMap1b, resolverMap2a, resolverMap2b}) + + /* + Schema + */ + + assertSnapshotSchemas := func(expectSchemas SchemaList, unexpectSchemas SchemaList) { + drain: + for { + select { + case snap = <-snapshots: + for _, expected := range expectSchemas { + if _, err := snap.Schemas.List().Find(expected.Metadata.Ref().Strings()); err != nil { + continue drain + } + } + for _, unexpected := range unexpectSchemas { + if _, err := snap.Schemas.List().Find(unexpected.Metadata.Ref().Strings()); err == nil { + continue drain + } + } + break drain + case err := <-errs: + Expect(err).NotTo(HaveOccurred()) + case <-time.After(time.Second * 10): + nsList1, _ := schemaClient.List(namespace1, clients.ListOpts{}) + nsList2, _ := schemaClient.List(namespace2, clients.ListOpts{}) + combined := SchemasByNamespace{ + namespace1: nsList1, + namespace2: nsList2, + } + Fail("expected final snapshot before 10 seconds. expected " + log.Sprintf("%v", combined)) + } + } + } + schema1a, err := schemaClient.Write(NewSchema(namespace1, name1), clients.WriteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + schema1b, err := schemaClient.Write(NewSchema(namespace2, name1), clients.WriteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + + assertSnapshotSchemas(SchemaList{schema1a, schema1b}, nil) + schema2a, err := schemaClient.Write(NewSchema(namespace1, name2), clients.WriteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + schema2b, err := schemaClient.Write(NewSchema(namespace2, name2), clients.WriteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + + assertSnapshotSchemas(SchemaList{schema1a, schema1b, schema2a, schema2b}, nil) + + err = schemaClient.Delete(schema2a.Metadata.Namespace, schema2a.Metadata.Name, clients.DeleteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + err = schemaClient.Delete(schema2b.Metadata.Namespace, schema2b.Metadata.Name, clients.DeleteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + + assertSnapshotSchemas(SchemaList{schema1a, schema1b}, SchemaList{schema2a, schema2b}) + + err = schemaClient.Delete(schema1a.Metadata.Namespace, schema1a.Metadata.Name, clients.DeleteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + err = schemaClient.Delete(schema1b.Metadata.Namespace, schema1b.Metadata.Name, clients.DeleteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + + assertSnapshotSchemas(nil, SchemaList{schema1a, schema1b, schema2a, schema2b}) + }) + It("tracks snapshots on changes to any resource using AllNamespace", func() { + ctx := context.Background() + err := emitter.Register() + Expect(err).NotTo(HaveOccurred()) + + snapshots, errs, err := emitter.Snapshots([]string{""}, clients.WatchOpts{ + Ctx: ctx, + RefreshRate: time.Second, + }) + Expect(err).NotTo(HaveOccurred()) + + var snap *ApiSnapshot + + /* + ResolverMap + */ + + assertSnapshotResolverMaps := func(expectResolverMaps ResolverMapList, unexpectResolverMaps ResolverMapList) { + drain: + for { + select { + case snap = <-snapshots: + for _, expected := range expectResolverMaps { + if _, err := snap.ResolverMaps.List().Find(expected.Metadata.Ref().Strings()); err != nil { + continue drain + } + } + for _, unexpected := range unexpectResolverMaps { + if _, err := snap.ResolverMaps.List().Find(unexpected.Metadata.Ref().Strings()); err == nil { + continue drain + } + } + break drain + case err := <-errs: + Expect(err).NotTo(HaveOccurred()) + case <-time.After(time.Second * 10): + nsList1, _ := resolverMapClient.List(namespace1, clients.ListOpts{}) + nsList2, _ := resolverMapClient.List(namespace2, clients.ListOpts{}) + combined := ResolverMapsByNamespace{ + namespace1: nsList1, + namespace2: nsList2, + } + Fail("expected final snapshot before 10 seconds. expected " + log.Sprintf("%v", combined)) + } + } + } + resolverMap1a, err := resolverMapClient.Write(NewResolverMap(namespace1, name1), clients.WriteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + resolverMap1b, err := resolverMapClient.Write(NewResolverMap(namespace2, name1), clients.WriteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + + assertSnapshotResolverMaps(ResolverMapList{resolverMap1a, resolverMap1b}, nil) + resolverMap2a, err := resolverMapClient.Write(NewResolverMap(namespace1, name2), clients.WriteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + resolverMap2b, err := resolverMapClient.Write(NewResolverMap(namespace2, name2), clients.WriteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + + assertSnapshotResolverMaps(ResolverMapList{resolverMap1a, resolverMap1b, resolverMap2a, resolverMap2b}, nil) + + err = resolverMapClient.Delete(resolverMap2a.Metadata.Namespace, resolverMap2a.Metadata.Name, clients.DeleteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + err = resolverMapClient.Delete(resolverMap2b.Metadata.Namespace, resolverMap2b.Metadata.Name, clients.DeleteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + + assertSnapshotResolverMaps(ResolverMapList{resolverMap1a, resolverMap1b}, ResolverMapList{resolverMap2a, resolverMap2b}) + + err = resolverMapClient.Delete(resolverMap1a.Metadata.Namespace, resolverMap1a.Metadata.Name, clients.DeleteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + err = resolverMapClient.Delete(resolverMap1b.Metadata.Namespace, resolverMap1b.Metadata.Name, clients.DeleteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + + assertSnapshotResolverMaps(nil, ResolverMapList{resolverMap1a, resolverMap1b, resolverMap2a, resolverMap2b}) + + /* + Schema + */ + + assertSnapshotSchemas := func(expectSchemas SchemaList, unexpectSchemas SchemaList) { + drain: + for { + select { + case snap = <-snapshots: + for _, expected := range expectSchemas { + if _, err := snap.Schemas.List().Find(expected.Metadata.Ref().Strings()); err != nil { + continue drain + } + } + for _, unexpected := range unexpectSchemas { + if _, err := snap.Schemas.List().Find(unexpected.Metadata.Ref().Strings()); err == nil { + continue drain + } + } + break drain + case err := <-errs: + Expect(err).NotTo(HaveOccurred()) + case <-time.After(time.Second * 10): + nsList1, _ := schemaClient.List(namespace1, clients.ListOpts{}) + nsList2, _ := schemaClient.List(namespace2, clients.ListOpts{}) + combined := SchemasByNamespace{ + namespace1: nsList1, + namespace2: nsList2, + } + Fail("expected final snapshot before 10 seconds. expected " + log.Sprintf("%v", combined)) + } + } + } + schema1a, err := schemaClient.Write(NewSchema(namespace1, name1), clients.WriteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + schema1b, err := schemaClient.Write(NewSchema(namespace2, name1), clients.WriteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + + assertSnapshotSchemas(SchemaList{schema1a, schema1b}, nil) + schema2a, err := schemaClient.Write(NewSchema(namespace1, name2), clients.WriteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + schema2b, err := schemaClient.Write(NewSchema(namespace2, name2), clients.WriteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + + assertSnapshotSchemas(SchemaList{schema1a, schema1b, schema2a, schema2b}, nil) + + err = schemaClient.Delete(schema2a.Metadata.Namespace, schema2a.Metadata.Name, clients.DeleteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + err = schemaClient.Delete(schema2b.Metadata.Namespace, schema2b.Metadata.Name, clients.DeleteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + + assertSnapshotSchemas(SchemaList{schema1a, schema1b}, SchemaList{schema2a, schema2b}) + + err = schemaClient.Delete(schema1a.Metadata.Namespace, schema1a.Metadata.Name, clients.DeleteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + err = schemaClient.Delete(schema1b.Metadata.Namespace, schema1b.Metadata.Name, clients.DeleteOpts{Ctx: ctx}) + Expect(err).NotTo(HaveOccurred()) + + assertSnapshotSchemas(nil, SchemaList{schema1a, schema1b, schema2a, schema2b}) + }) +}) diff --git a/pkg/api/v1/resolver_map.pb.go b/pkg/api/v1/resolver_map.pb.go new file mode 100644 index 0000000..5d90bbd --- /dev/null +++ b/pkg/api/v1/resolver_map.pb.go @@ -0,0 +1,967 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: github.com/solo-io/sqoop/api/v1/resolver_map.proto + +package v1 + +import ( + bytes "bytes" + fmt "fmt" + math "math" + + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + v1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1" + core "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +// +// The ResolverMap object maps Resolvers to the fields in the GraphQL Schema +// The skeleton of a Resolver Map will be generated by Sqoop automatically when a schema +// is read or updated if one does not alreay exist. +type ResolverMap struct { + // Types is a map of Type Names (defined in the schema) to a TypeResolver, which contain resolvers for the + // specific fields of the type + Types map[string]*TypeResolver `protobuf:"bytes,3,rep,name=types,proto3" json:"types,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // Status indicates the validation status of this resource. + // Status is read-only by clients, and set by gloo during validation + Status core.Status `protobuf:"bytes,6,opt,name=status,proto3" json:"status" testdiff:"ignore"` + // Metadata contains the object metadata for this resource + Metadata core.Metadata `protobuf:"bytes,7,opt,name=metadata,proto3" json:"metadata"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ResolverMap) Reset() { *m = ResolverMap{} } +func (m *ResolverMap) String() string { return proto.CompactTextString(m) } +func (*ResolverMap) ProtoMessage() {} +func (*ResolverMap) Descriptor() ([]byte, []int) { + return fileDescriptor_bf560cdc02a8e0a9, []int{0} +} +func (m *ResolverMap) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ResolverMap.Unmarshal(m, b) +} +func (m *ResolverMap) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ResolverMap.Marshal(b, m, deterministic) +} +func (m *ResolverMap) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResolverMap.Merge(m, src) +} +func (m *ResolverMap) XXX_Size() int { + return xxx_messageInfo_ResolverMap.Size(m) +} +func (m *ResolverMap) XXX_DiscardUnknown() { + xxx_messageInfo_ResolverMap.DiscardUnknown(m) +} + +var xxx_messageInfo_ResolverMap proto.InternalMessageInfo + +func (m *ResolverMap) GetTypes() map[string]*TypeResolver { + if m != nil { + return m.Types + } + return nil +} + +func (m *ResolverMap) GetStatus() core.Status { + if m != nil { + return m.Status + } + return core.Status{} +} + +func (m *ResolverMap) GetMetadata() core.Metadata { + if m != nil { + return m.Metadata + } + return core.Metadata{} +} + +// TypeResolver contains the individual resolvers for each field for a specific type +type TypeResolver struct { + // This is a map of Field Names to the resolver that Sqoop should invoke when a query arrives for that field + Fields map[string]*FieldResolver `protobuf:"bytes,1,rep,name=fields,proto3" json:"fields,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TypeResolver) Reset() { *m = TypeResolver{} } +func (m *TypeResolver) String() string { return proto.CompactTextString(m) } +func (*TypeResolver) ProtoMessage() {} +func (*TypeResolver) Descriptor() ([]byte, []int) { + return fileDescriptor_bf560cdc02a8e0a9, []int{1} +} +func (m *TypeResolver) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TypeResolver.Unmarshal(m, b) +} +func (m *TypeResolver) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TypeResolver.Marshal(b, m, deterministic) +} +func (m *TypeResolver) XXX_Merge(src proto.Message) { + xxx_messageInfo_TypeResolver.Merge(m, src) +} +func (m *TypeResolver) XXX_Size() int { + return xxx_messageInfo_TypeResolver.Size(m) +} +func (m *TypeResolver) XXX_DiscardUnknown() { + xxx_messageInfo_TypeResolver.DiscardUnknown(m) +} + +var xxx_messageInfo_TypeResolver proto.InternalMessageInfo + +func (m *TypeResolver) GetFields() map[string]*FieldResolver { + if m != nil { + return m.Fields + } + return nil +} + +// Resolvers define the actual logic Sqoop needs to know in order to resolve a specific field query +type FieldResolver struct { + // a resolver can have one of three types: + // + // Types that are valid to be assigned to Resolver: + // *FieldResolver_GlooResolver + // *FieldResolver_TemplateResolver + // *FieldResolver_NodejsResolver + Resolver isFieldResolver_Resolver `protobuf_oneof:"resolver"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *FieldResolver) Reset() { *m = FieldResolver{} } +func (m *FieldResolver) String() string { return proto.CompactTextString(m) } +func (*FieldResolver) ProtoMessage() {} +func (*FieldResolver) Descriptor() ([]byte, []int) { + return fileDescriptor_bf560cdc02a8e0a9, []int{2} +} +func (m *FieldResolver) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_FieldResolver.Unmarshal(m, b) +} +func (m *FieldResolver) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_FieldResolver.Marshal(b, m, deterministic) +} +func (m *FieldResolver) XXX_Merge(src proto.Message) { + xxx_messageInfo_FieldResolver.Merge(m, src) +} +func (m *FieldResolver) XXX_Size() int { + return xxx_messageInfo_FieldResolver.Size(m) +} +func (m *FieldResolver) XXX_DiscardUnknown() { + xxx_messageInfo_FieldResolver.DiscardUnknown(m) +} + +var xxx_messageInfo_FieldResolver proto.InternalMessageInfo + +type isFieldResolver_Resolver interface { + isFieldResolver_Resolver() + Equal(interface{}) bool +} + +type FieldResolver_GlooResolver struct { + GlooResolver *GlooResolver `protobuf:"bytes,1,opt,name=gloo_resolver,json=glooResolver,proto3,oneof"` +} +type FieldResolver_TemplateResolver struct { + TemplateResolver *TemplateResolver `protobuf:"bytes,2,opt,name=template_resolver,json=templateResolver,proto3,oneof"` +} +type FieldResolver_NodejsResolver struct { + NodejsResolver *NodeJSResolver `protobuf:"bytes,3,opt,name=nodejs_resolver,json=nodejsResolver,proto3,oneof"` +} + +func (*FieldResolver_GlooResolver) isFieldResolver_Resolver() {} +func (*FieldResolver_TemplateResolver) isFieldResolver_Resolver() {} +func (*FieldResolver_NodejsResolver) isFieldResolver_Resolver() {} + +func (m *FieldResolver) GetResolver() isFieldResolver_Resolver { + if m != nil { + return m.Resolver + } + return nil +} + +func (m *FieldResolver) GetGlooResolver() *GlooResolver { + if x, ok := m.GetResolver().(*FieldResolver_GlooResolver); ok { + return x.GlooResolver + } + return nil +} + +func (m *FieldResolver) GetTemplateResolver() *TemplateResolver { + if x, ok := m.GetResolver().(*FieldResolver_TemplateResolver); ok { + return x.TemplateResolver + } + return nil +} + +func (m *FieldResolver) GetNodejsResolver() *NodeJSResolver { + if x, ok := m.GetResolver().(*FieldResolver_NodejsResolver); ok { + return x.NodejsResolver + } + return nil +} + +// XXX_OneofFuncs is for the internal use of the proto package. +func (*FieldResolver) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { + return _FieldResolver_OneofMarshaler, _FieldResolver_OneofUnmarshaler, _FieldResolver_OneofSizer, []interface{}{ + (*FieldResolver_GlooResolver)(nil), + (*FieldResolver_TemplateResolver)(nil), + (*FieldResolver_NodejsResolver)(nil), + } +} + +func _FieldResolver_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { + m := msg.(*FieldResolver) + // resolver + switch x := m.Resolver.(type) { + case *FieldResolver_GlooResolver: + _ = b.EncodeVarint(1<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.GlooResolver); err != nil { + return err + } + case *FieldResolver_TemplateResolver: + _ = b.EncodeVarint(2<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.TemplateResolver); err != nil { + return err + } + case *FieldResolver_NodejsResolver: + _ = b.EncodeVarint(3<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.NodejsResolver); err != nil { + return err + } + case nil: + default: + return fmt.Errorf("FieldResolver.Resolver has unexpected type %T", x) + } + return nil +} + +func _FieldResolver_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { + m := msg.(*FieldResolver) + switch tag { + case 1: // resolver.gloo_resolver + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(GlooResolver) + err := b.DecodeMessage(msg) + m.Resolver = &FieldResolver_GlooResolver{msg} + return true, err + case 2: // resolver.template_resolver + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(TemplateResolver) + err := b.DecodeMessage(msg) + m.Resolver = &FieldResolver_TemplateResolver{msg} + return true, err + case 3: // resolver.nodejs_resolver + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(NodeJSResolver) + err := b.DecodeMessage(msg) + m.Resolver = &FieldResolver_NodejsResolver{msg} + return true, err + default: + return false, nil + } +} + +func _FieldResolver_OneofSizer(msg proto.Message) (n int) { + m := msg.(*FieldResolver) + // resolver + switch x := m.Resolver.(type) { + case *FieldResolver_GlooResolver: + s := proto.Size(x.GlooResolver) + n += 1 // tag and wire + n += proto.SizeVarint(uint64(s)) + n += s + case *FieldResolver_TemplateResolver: + s := proto.Size(x.TemplateResolver) + n += 1 // tag and wire + n += proto.SizeVarint(uint64(s)) + n += s + case *FieldResolver_NodejsResolver: + s := proto.Size(x.NodejsResolver) + n += 1 // tag and wire + n += proto.SizeVarint(uint64(s)) + n += s + case nil: + default: + panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) + } + return n +} + +// GlooResolvers are the "meat" of Sqoop. GlooResolvers tell Sqoop how to invoke a "Gloo Function" +type GlooResolver struct { + // the Request Template, if specified, will become the body of the HTTP request used to invoke a function through Gloo + // input parameters, if needed, should be specified in the request template. See Sqoop's [Resolver documentation] + // for more information on Request Templates. + RequestTemplate *RequestTemplate `protobuf:"bytes,1,opt,name=request_template,json=requestTemplate,proto3" json:"request_template,omitempty"` + // The response template, if specified, will transform the body of HTTP responses returned by Gloo functions. + // This field should be used if the object returned by the Gloo Function does not match the type specified in the GraphQL schema. + // It can also be used to modify or transform responses from their original state. See Sqoop's [Resolver documentation] + // for more information on Response Templates. + ResponseTemplate *ResponseTemplate `protobuf:"bytes,2,opt,name=response_template,json=responseTemplate,proto3" json:"response_template,omitempty"` + // the routing action to take when resolver is executed. usually this is a Route destination + Action *v1.RouteAction `protobuf:"bytes,4,opt,name=action,proto3" json:"action,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GlooResolver) Reset() { *m = GlooResolver{} } +func (m *GlooResolver) String() string { return proto.CompactTextString(m) } +func (*GlooResolver) ProtoMessage() {} +func (*GlooResolver) Descriptor() ([]byte, []int) { + return fileDescriptor_bf560cdc02a8e0a9, []int{3} +} +func (m *GlooResolver) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GlooResolver.Unmarshal(m, b) +} +func (m *GlooResolver) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GlooResolver.Marshal(b, m, deterministic) +} +func (m *GlooResolver) XXX_Merge(src proto.Message) { + xxx_messageInfo_GlooResolver.Merge(m, src) +} +func (m *GlooResolver) XXX_Size() int { + return xxx_messageInfo_GlooResolver.Size(m) +} +func (m *GlooResolver) XXX_DiscardUnknown() { + xxx_messageInfo_GlooResolver.DiscardUnknown(m) +} + +var xxx_messageInfo_GlooResolver proto.InternalMessageInfo + +func (m *GlooResolver) GetRequestTemplate() *RequestTemplate { + if m != nil { + return m.RequestTemplate + } + return nil +} + +func (m *GlooResolver) GetResponseTemplate() *ResponseTemplate { + if m != nil { + return m.ResponseTemplate + } + return nil +} + +func (m *GlooResolver) GetAction() *v1.RouteAction { + if m != nil { + return m.Action + } + return nil +} + +type RequestTemplate struct { + Verb string `protobuf:"bytes,1,opt,name=verb,proto3" json:"verb,omitempty"` + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + Body string `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"` + Headers map[string]string `protobuf:"bytes,4,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RequestTemplate) Reset() { *m = RequestTemplate{} } +func (m *RequestTemplate) String() string { return proto.CompactTextString(m) } +func (*RequestTemplate) ProtoMessage() {} +func (*RequestTemplate) Descriptor() ([]byte, []int) { + return fileDescriptor_bf560cdc02a8e0a9, []int{4} +} +func (m *RequestTemplate) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RequestTemplate.Unmarshal(m, b) +} +func (m *RequestTemplate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RequestTemplate.Marshal(b, m, deterministic) +} +func (m *RequestTemplate) XXX_Merge(src proto.Message) { + xxx_messageInfo_RequestTemplate.Merge(m, src) +} +func (m *RequestTemplate) XXX_Size() int { + return xxx_messageInfo_RequestTemplate.Size(m) +} +func (m *RequestTemplate) XXX_DiscardUnknown() { + xxx_messageInfo_RequestTemplate.DiscardUnknown(m) +} + +var xxx_messageInfo_RequestTemplate proto.InternalMessageInfo + +func (m *RequestTemplate) GetVerb() string { + if m != nil { + return m.Verb + } + return "" +} + +func (m *RequestTemplate) GetPath() string { + if m != nil { + return m.Path + } + return "" +} + +func (m *RequestTemplate) GetBody() string { + if m != nil { + return m.Body + } + return "" +} + +func (m *RequestTemplate) GetHeaders() map[string]string { + if m != nil { + return m.Headers + } + return nil +} + +type ResponseTemplate struct { + Body string `protobuf:"bytes,2,opt,name=body,proto3" json:"body,omitempty"` + Headers map[string]string `protobuf:"bytes,3,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ResponseTemplate) Reset() { *m = ResponseTemplate{} } +func (m *ResponseTemplate) String() string { return proto.CompactTextString(m) } +func (*ResponseTemplate) ProtoMessage() {} +func (*ResponseTemplate) Descriptor() ([]byte, []int) { + return fileDescriptor_bf560cdc02a8e0a9, []int{5} +} +func (m *ResponseTemplate) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ResponseTemplate.Unmarshal(m, b) +} +func (m *ResponseTemplate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ResponseTemplate.Marshal(b, m, deterministic) +} +func (m *ResponseTemplate) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResponseTemplate.Merge(m, src) +} +func (m *ResponseTemplate) XXX_Size() int { + return xxx_messageInfo_ResponseTemplate.Size(m) +} +func (m *ResponseTemplate) XXX_DiscardUnknown() { + xxx_messageInfo_ResponseTemplate.DiscardUnknown(m) +} + +var xxx_messageInfo_ResponseTemplate proto.InternalMessageInfo + +func (m *ResponseTemplate) GetBody() string { + if m != nil { + return m.Body + } + return "" +} + +func (m *ResponseTemplate) GetHeaders() map[string]string { + if m != nil { + return m.Headers + } + return nil +} + +// A Go-template which will return data for a Resolver without making a function call. Template Resolvers can make use +// of Sqoop's builtin template functions as well as the data provided by the Params object to the resolver. +// Read more about Templates and Resolvers in Sqoop\'s [Resolver documentation]. +type TemplateResolver struct { + // the Go template as an inline string + InlineTemplate string `protobuf:"bytes,1,opt,name=inline_template,json=inlineTemplate,proto3" json:"inline_template,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TemplateResolver) Reset() { *m = TemplateResolver{} } +func (m *TemplateResolver) String() string { return proto.CompactTextString(m) } +func (*TemplateResolver) ProtoMessage() {} +func (*TemplateResolver) Descriptor() ([]byte, []int) { + return fileDescriptor_bf560cdc02a8e0a9, []int{6} +} +func (m *TemplateResolver) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TemplateResolver.Unmarshal(m, b) +} +func (m *TemplateResolver) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TemplateResolver.Marshal(b, m, deterministic) +} +func (m *TemplateResolver) XXX_Merge(src proto.Message) { + xxx_messageInfo_TemplateResolver.Merge(m, src) +} +func (m *TemplateResolver) XXX_Size() int { + return xxx_messageInfo_TemplateResolver.Size(m) +} +func (m *TemplateResolver) XXX_DiscardUnknown() { + xxx_messageInfo_TemplateResolver.DiscardUnknown(m) +} + +var xxx_messageInfo_TemplateResolver proto.InternalMessageInfo + +func (m *TemplateResolver) GetInlineTemplate() string { + if m != nil { + return m.InlineTemplate + } + return "" +} + +// NOTE: currently unsupported +type NodeJSResolver struct { + InlineCode string `protobuf:"bytes,1,opt,name=inline_code,json=inlineCode,proto3" json:"inline_code,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *NodeJSResolver) Reset() { *m = NodeJSResolver{} } +func (m *NodeJSResolver) String() string { return proto.CompactTextString(m) } +func (*NodeJSResolver) ProtoMessage() {} +func (*NodeJSResolver) Descriptor() ([]byte, []int) { + return fileDescriptor_bf560cdc02a8e0a9, []int{7} +} +func (m *NodeJSResolver) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_NodeJSResolver.Unmarshal(m, b) +} +func (m *NodeJSResolver) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_NodeJSResolver.Marshal(b, m, deterministic) +} +func (m *NodeJSResolver) XXX_Merge(src proto.Message) { + xxx_messageInfo_NodeJSResolver.Merge(m, src) +} +func (m *NodeJSResolver) XXX_Size() int { + return xxx_messageInfo_NodeJSResolver.Size(m) +} +func (m *NodeJSResolver) XXX_DiscardUnknown() { + xxx_messageInfo_NodeJSResolver.DiscardUnknown(m) +} + +var xxx_messageInfo_NodeJSResolver proto.InternalMessageInfo + +func (m *NodeJSResolver) GetInlineCode() string { + if m != nil { + return m.InlineCode + } + return "" +} + +func init() { + proto.RegisterType((*ResolverMap)(nil), "sqoop.solo.io.ResolverMap") + proto.RegisterMapType((map[string]*TypeResolver)(nil), "sqoop.solo.io.ResolverMap.TypesEntry") + proto.RegisterType((*TypeResolver)(nil), "sqoop.solo.io.TypeResolver") + proto.RegisterMapType((map[string]*FieldResolver)(nil), "sqoop.solo.io.TypeResolver.FieldsEntry") + proto.RegisterType((*FieldResolver)(nil), "sqoop.solo.io.FieldResolver") + proto.RegisterType((*GlooResolver)(nil), "sqoop.solo.io.GlooResolver") + proto.RegisterType((*RequestTemplate)(nil), "sqoop.solo.io.RequestTemplate") + proto.RegisterMapType((map[string]string)(nil), "sqoop.solo.io.RequestTemplate.HeadersEntry") + proto.RegisterType((*ResponseTemplate)(nil), "sqoop.solo.io.ResponseTemplate") + proto.RegisterMapType((map[string]string)(nil), "sqoop.solo.io.ResponseTemplate.HeadersEntry") + proto.RegisterType((*TemplateResolver)(nil), "sqoop.solo.io.TemplateResolver") + proto.RegisterType((*NodeJSResolver)(nil), "sqoop.solo.io.NodeJSResolver") +} + +func init() { + proto.RegisterFile("github.com/solo-io/sqoop/api/v1/resolver_map.proto", fileDescriptor_bf560cdc02a8e0a9) +} + +var fileDescriptor_bf560cdc02a8e0a9 = []byte{ + // 732 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0x4d, 0x4f, 0xdb, 0x4a, + 0x14, 0xc5, 0x49, 0x08, 0xe4, 0x26, 0x21, 0x61, 0x84, 0x9e, 0x4c, 0x78, 0x8f, 0x20, 0x3f, 0x3d, + 0xc1, 0x53, 0x5b, 0x5b, 0x49, 0x37, 0x28, 0x2c, 0xaa, 0xa6, 0xe2, 0xa3, 0x55, 0x61, 0x61, 0x5a, + 0x55, 0xea, 0x06, 0x39, 0xf1, 0x10, 0x0c, 0x4e, 0xae, 0x99, 0x99, 0x44, 0xcd, 0x96, 0x5f, 0x83, + 0xfa, 0x2f, 0xba, 0xeb, 0xb2, 0xeb, 0x2e, 0x58, 0xf4, 0x1f, 0xd0, 0x5d, 0x77, 0x95, 0xc7, 0x76, + 0x62, 0x9b, 0x94, 0xb6, 0x8b, 0xae, 0x32, 0xbe, 0xf7, 0x9c, 0x33, 0xf7, 0x1e, 0x1d, 0x4d, 0xa0, + 0xd9, 0x73, 0xc4, 0xd9, 0xb0, 0xa3, 0x77, 0xb1, 0x6f, 0x70, 0x74, 0xf1, 0x91, 0x83, 0x06, 0xbf, + 0x44, 0xf4, 0x0c, 0xcb, 0x73, 0x8c, 0x51, 0xc3, 0x60, 0x94, 0xa3, 0x3b, 0xa2, 0xec, 0xa4, 0x6f, + 0x79, 0xba, 0xc7, 0x50, 0x20, 0x29, 0x4b, 0x80, 0xee, 0xc3, 0x75, 0x07, 0x6b, 0x2b, 0x3d, 0xec, + 0xa1, 0xec, 0x18, 0xfe, 0x29, 0x00, 0xd5, 0x1a, 0xb3, 0x84, 0xfd, 0xdf, 0x0b, 0x47, 0x44, 0xda, + 0x7d, 0x2a, 0x2c, 0xdb, 0x12, 0x56, 0x48, 0x31, 0x7e, 0x81, 0xc2, 0x85, 0x25, 0x86, 0xfc, 0x37, + 0xee, 0x88, 0xbe, 0x43, 0xca, 0xf6, 0x0c, 0x4a, 0xcf, 0x45, 0x34, 0x3c, 0x86, 0xe7, 0xb4, 0x2b, + 0x78, 0xf0, 0x15, 0x92, 0x3d, 0x86, 0xef, 0xc6, 0x01, 0x53, 0xfb, 0x90, 0x81, 0xa2, 0x19, 0x9a, + 0x71, 0x68, 0x79, 0x64, 0x07, 0xe6, 0xc5, 0xd8, 0xa3, 0x5c, 0xcd, 0x6e, 0x64, 0xb7, 0x8a, 0xcd, + 0xff, 0xf4, 0x84, 0x2b, 0x7a, 0x0c, 0xaa, 0xbf, 0xf2, 0x71, 0xbb, 0x03, 0xc1, 0xc6, 0x66, 0xc0, + 0x21, 0xfb, 0x90, 0x0f, 0x36, 0x51, 0xf3, 0x1b, 0xca, 0x56, 0xb1, 0xb9, 0xa2, 0x77, 0x91, 0xd1, + 0x09, 0xf9, 0x58, 0xf6, 0xda, 0xab, 0x1f, 0x6f, 0xea, 0x73, 0x5f, 0x6f, 0xea, 0xcb, 0x82, 0x72, + 0x61, 0x3b, 0xa7, 0xa7, 0x2d, 0xcd, 0xe9, 0x0d, 0x90, 0x51, 0xcd, 0x0c, 0xe9, 0x64, 0x1b, 0x16, + 0x23, 0x17, 0xd5, 0x05, 0x29, 0xf5, 0x57, 0x52, 0xea, 0x30, 0xec, 0xb6, 0x73, 0xbe, 0x98, 0x39, + 0x41, 0xd7, 0x5e, 0x03, 0x4c, 0xe7, 0x22, 0x55, 0xc8, 0x5e, 0xd0, 0xb1, 0xaa, 0x6c, 0x28, 0x5b, + 0x05, 0xd3, 0x3f, 0x92, 0x06, 0xcc, 0x8f, 0x2c, 0x77, 0x48, 0xd5, 0x8c, 0x94, 0x5d, 0x4b, 0xed, + 0xe7, 0x73, 0xa3, 0x1d, 0xcd, 0x00, 0xd9, 0xca, 0x6c, 0x2b, 0xad, 0xb5, 0xab, 0xdb, 0x5c, 0x0e, + 0x32, 0xac, 0x7f, 0x75, 0x9b, 0xab, 0x90, 0x72, 0x3c, 0x3e, 0x5c, 0xbb, 0x56, 0xa0, 0x14, 0x27, + 0x92, 0x27, 0x90, 0x3f, 0x75, 0xa8, 0x6b, 0x73, 0x55, 0x91, 0x2e, 0x6e, 0xde, 0x73, 0x8b, 0xbe, + 0x27, 0x91, 0x81, 0x8f, 0x21, 0xad, 0xf6, 0x06, 0x8a, 0xb1, 0xf2, 0x8c, 0x35, 0x9a, 0xc9, 0x35, + 0xfe, 0x4e, 0x5d, 0x20, 0xc9, 0x33, 0xf6, 0xd0, 0xbe, 0x29, 0x50, 0x4e, 0x34, 0x49, 0x1b, 0xca, + 0x7e, 0x36, 0x4e, 0xa2, 0x95, 0xe4, 0x2d, 0x77, 0x8d, 0xd9, 0x77, 0x11, 0x23, 0xce, 0xc1, 0x9c, + 0x59, 0xea, 0xc5, 0xbe, 0xc9, 0x11, 0x2c, 0x0b, 0xda, 0xf7, 0x5c, 0x4b, 0xd0, 0xa9, 0x4e, 0x30, + 0x59, 0x3d, 0xbd, 0x7a, 0x88, 0x8b, 0x69, 0x55, 0x45, 0xaa, 0x46, 0x0e, 0xa0, 0x32, 0x40, 0x9b, + 0x9e, 0xf3, 0xa9, 0x5a, 0x56, 0xaa, 0xfd, 0x93, 0x52, 0x3b, 0x42, 0x9b, 0xbe, 0x38, 0x8e, 0x69, + 0x2d, 0x05, 0xbc, 0xa8, 0xd2, 0x06, 0x58, 0x8c, 0x24, 0xb4, 0xcf, 0x0a, 0x94, 0xe2, 0x6b, 0x90, + 0xe7, 0x50, 0x65, 0xf4, 0x72, 0x48, 0xb9, 0x38, 0x89, 0x46, 0x08, 0xb7, 0x5f, 0xbf, 0x13, 0x7b, + 0x09, 0x9b, 0x0c, 0x5f, 0x61, 0xc9, 0x02, 0x79, 0x09, 0xcb, 0x8c, 0x72, 0x0f, 0x07, 0x9c, 0x4e, + 0xb5, 0x66, 0x3b, 0x60, 0x86, 0xb8, 0x89, 0x58, 0x95, 0xa5, 0x2a, 0xa4, 0x01, 0x79, 0xab, 0x2b, + 0x1c, 0x1c, 0xa8, 0x39, 0x29, 0xb1, 0xaa, 0xfb, 0x76, 0x4f, 0x15, 0x70, 0x28, 0xe8, 0x53, 0x09, + 0x30, 0x43, 0xa0, 0xf6, 0x49, 0x81, 0x4a, 0x6a, 0x4a, 0x42, 0x20, 0x37, 0xa2, 0xac, 0x13, 0xe6, + 0x46, 0x9e, 0xfd, 0x9a, 0x67, 0x89, 0x33, 0x39, 0x5b, 0xc1, 0x94, 0x67, 0xbf, 0xd6, 0x41, 0x7b, + 0x2c, 0x3d, 0x2e, 0x98, 0xf2, 0x4c, 0x76, 0x61, 0xe1, 0x8c, 0x5a, 0x36, 0x65, 0x5c, 0xcd, 0xc9, + 0x0c, 0x3f, 0xb8, 0xdf, 0x12, 0xfd, 0x20, 0x40, 0x07, 0x39, 0x8e, 0xb8, 0xb5, 0x16, 0x94, 0xe2, + 0x8d, 0x19, 0x49, 0x5e, 0x89, 0x27, 0xb9, 0x10, 0xcf, 0xea, 0x7b, 0x05, 0xaa, 0x69, 0xb3, 0x26, + 0xb3, 0x66, 0x62, 0xb3, 0xee, 0x4d, 0x67, 0x0d, 0x5e, 0xad, 0x87, 0x3f, 0xb1, 0xfc, 0x0f, 0x0c, + 0xbb, 0x03, 0xd5, 0x74, 0xb4, 0xc9, 0x26, 0x54, 0x9c, 0x81, 0xeb, 0x0c, 0x68, 0x32, 0x5e, 0x05, + 0x73, 0x29, 0x28, 0x47, 0x04, 0xad, 0x01, 0x4b, 0xc9, 0x24, 0x93, 0x3a, 0x14, 0x43, 0x6a, 0x17, + 0xed, 0x88, 0x06, 0x41, 0xe9, 0x19, 0xda, 0xb4, 0xfd, 0xff, 0xf5, 0x97, 0x75, 0xe5, 0xed, 0xbf, + 0x3f, 0xfc, 0x9f, 0xf3, 0x2e, 0x7a, 0xe1, 0x73, 0xdf, 0xc9, 0xcb, 0x97, 0xfe, 0xf1, 0xf7, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xde, 0x97, 0x2b, 0x34, 0x15, 0x07, 0x00, 0x00, +} + +func (this *ResolverMap) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*ResolverMap) + if !ok { + that2, ok := that.(ResolverMap) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if len(this.Types) != len(that1.Types) { + return false + } + for i := range this.Types { + if !this.Types[i].Equal(that1.Types[i]) { + return false + } + } + if !this.Status.Equal(&that1.Status) { + return false + } + if !this.Metadata.Equal(&that1.Metadata) { + return false + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } + return true +} +func (this *TypeResolver) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*TypeResolver) + if !ok { + that2, ok := that.(TypeResolver) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if len(this.Fields) != len(that1.Fields) { + return false + } + for i := range this.Fields { + if !this.Fields[i].Equal(that1.Fields[i]) { + return false + } + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } + return true +} +func (this *FieldResolver) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*FieldResolver) + if !ok { + that2, ok := that.(FieldResolver) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if that1.Resolver == nil { + if this.Resolver != nil { + return false + } + } else if this.Resolver == nil { + return false + } else if !this.Resolver.Equal(that1.Resolver) { + return false + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } + return true +} +func (this *FieldResolver_GlooResolver) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*FieldResolver_GlooResolver) + if !ok { + that2, ok := that.(FieldResolver_GlooResolver) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !this.GlooResolver.Equal(that1.GlooResolver) { + return false + } + return true +} +func (this *FieldResolver_TemplateResolver) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*FieldResolver_TemplateResolver) + if !ok { + that2, ok := that.(FieldResolver_TemplateResolver) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !this.TemplateResolver.Equal(that1.TemplateResolver) { + return false + } + return true +} +func (this *FieldResolver_NodejsResolver) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*FieldResolver_NodejsResolver) + if !ok { + that2, ok := that.(FieldResolver_NodejsResolver) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !this.NodejsResolver.Equal(that1.NodejsResolver) { + return false + } + return true +} +func (this *GlooResolver) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*GlooResolver) + if !ok { + that2, ok := that.(GlooResolver) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !this.RequestTemplate.Equal(that1.RequestTemplate) { + return false + } + if !this.ResponseTemplate.Equal(that1.ResponseTemplate) { + return false + } + if !this.Action.Equal(that1.Action) { + return false + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } + return true +} +func (this *RequestTemplate) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*RequestTemplate) + if !ok { + that2, ok := that.(RequestTemplate) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Verb != that1.Verb { + return false + } + if this.Path != that1.Path { + return false + } + if this.Body != that1.Body { + return false + } + if len(this.Headers) != len(that1.Headers) { + return false + } + for i := range this.Headers { + if this.Headers[i] != that1.Headers[i] { + return false + } + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } + return true +} +func (this *ResponseTemplate) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*ResponseTemplate) + if !ok { + that2, ok := that.(ResponseTemplate) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Body != that1.Body { + return false + } + if len(this.Headers) != len(that1.Headers) { + return false + } + for i := range this.Headers { + if this.Headers[i] != that1.Headers[i] { + return false + } + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } + return true +} +func (this *TemplateResolver) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*TemplateResolver) + if !ok { + that2, ok := that.(TemplateResolver) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.InlineTemplate != that1.InlineTemplate { + return false + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } + return true +} +func (this *NodeJSResolver) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*NodeJSResolver) + if !ok { + that2, ok := that.(NodeJSResolver) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.InlineCode != that1.InlineCode { + return false + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } + return true +} diff --git a/pkg/api/v1/resolver_map.sk.go b/pkg/api/v1/resolver_map.sk.go new file mode 100644 index 0000000..e48f7a0 --- /dev/null +++ b/pkg/api/v1/resolver_map.sk.go @@ -0,0 +1,167 @@ +// Code generated by solo-kit. DO NOT EDIT. + +package v1 + +import ( + "sort" + + "github.com/gogo/protobuf/proto" + "github.com/solo-io/solo-kit/pkg/api/v1/clients/kube/crd" + "github.com/solo-io/solo-kit/pkg/api/v1/resources" + "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" + "github.com/solo-io/solo-kit/pkg/errors" + "github.com/solo-io/solo-kit/pkg/utils/hashutils" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// TODO: modify as needed to populate additional fields +func NewResolverMap(namespace, name string) *ResolverMap { + return &ResolverMap{ + Metadata: core.Metadata{ + Name: name, + Namespace: namespace, + }, + } +} + +func (r *ResolverMap) SetStatus(status core.Status) { + r.Status = status +} + +func (r *ResolverMap) SetMetadata(meta core.Metadata) { + r.Metadata = meta +} + +func (r *ResolverMap) Hash() uint64 { + metaCopy := r.GetMetadata() + metaCopy.ResourceVersion = "" + return hashutils.HashAll( + metaCopy, + r.Types, + ) +} + +type ResolverMapList []*ResolverMap +type ResolverMapsByNamespace map[string]ResolverMapList + +// namespace is optional, if left empty, names can collide if the list contains more than one with the same name +func (list ResolverMapList) Find(namespace, name string) (*ResolverMap, error) { + for _, resolverMap := range list { + if resolverMap.Metadata.Name == name { + if namespace == "" || resolverMap.Metadata.Namespace == namespace { + return resolverMap, nil + } + } + } + return nil, errors.Errorf("list did not find resolverMap %v.%v", namespace, name) +} + +func (list ResolverMapList) AsResources() resources.ResourceList { + var ress resources.ResourceList + for _, resolverMap := range list { + ress = append(ress, resolverMap) + } + return ress +} + +func (list ResolverMapList) AsInputResources() resources.InputResourceList { + var ress resources.InputResourceList + for _, resolverMap := range list { + ress = append(ress, resolverMap) + } + return ress +} + +func (list ResolverMapList) Names() []string { + var names []string + for _, resolverMap := range list { + names = append(names, resolverMap.Metadata.Name) + } + return names +} + +func (list ResolverMapList) NamespacesDotNames() []string { + var names []string + for _, resolverMap := range list { + names = append(names, resolverMap.Metadata.Namespace+"."+resolverMap.Metadata.Name) + } + return names +} + +func (list ResolverMapList) Sort() ResolverMapList { + sort.SliceStable(list, func(i, j int) bool { + return list[i].Metadata.Less(list[j].Metadata) + }) + return list +} + +func (list ResolverMapList) Clone() ResolverMapList { + var resolverMapList ResolverMapList + for _, resolverMap := range list { + resolverMapList = append(resolverMapList, proto.Clone(resolverMap).(*ResolverMap)) + } + return resolverMapList +} + +func (list ResolverMapList) Each(f func(element *ResolverMap)) { + for _, resolverMap := range list { + f(resolverMap) + } +} + +func (list ResolverMapList) AsInterfaces() []interface{} { + var asInterfaces []interface{} + list.Each(func(element *ResolverMap) { + asInterfaces = append(asInterfaces, element) + }) + return asInterfaces +} + +func (byNamespace ResolverMapsByNamespace) Add(resolverMap ...*ResolverMap) { + for _, item := range resolverMap { + byNamespace[item.Metadata.Namespace] = append(byNamespace[item.Metadata.Namespace], item) + } +} + +func (byNamespace ResolverMapsByNamespace) Clear(namespace string) { + delete(byNamespace, namespace) +} + +func (byNamespace ResolverMapsByNamespace) List() ResolverMapList { + var list ResolverMapList + for _, resolverMapList := range byNamespace { + list = append(list, resolverMapList...) + } + return list.Sort() +} + +func (byNamespace ResolverMapsByNamespace) Clone() ResolverMapsByNamespace { + cloned := make(ResolverMapsByNamespace) + for ns, list := range byNamespace { + cloned[ns] = list.Clone() + } + return cloned +} + +var _ resources.Resource = &ResolverMap{} + +// Kubernetes Adapter for ResolverMap + +func (o *ResolverMap) GetObjectKind() schema.ObjectKind { + t := ResolverMapCrd.TypeMeta() + return &t +} + +func (o *ResolverMap) DeepCopyObject() runtime.Object { + return resources.Clone(o).(*ResolverMap) +} + +var ResolverMapCrd = crd.NewCrd("sqoop.solo.io", + "resolvermaps", + "sqoop.solo.io", + "v1", + "ResolverMap", + "rm", + false, + &ResolverMap{}) diff --git a/pkg/api/v1/resolver_map_client.sk.go b/pkg/api/v1/resolver_map_client.sk.go new file mode 100644 index 0000000..3656160 --- /dev/null +++ b/pkg/api/v1/resolver_map_client.sk.go @@ -0,0 +1,118 @@ +// Code generated by solo-kit. DO NOT EDIT. + +package v1 + +import ( + "github.com/solo-io/solo-kit/pkg/api/v1/clients" + "github.com/solo-io/solo-kit/pkg/api/v1/clients/factory" + "github.com/solo-io/solo-kit/pkg/api/v1/resources" + "github.com/solo-io/solo-kit/pkg/errors" +) + +type ResolverMapClient interface { + BaseClient() clients.ResourceClient + Register() error + Read(namespace, name string, opts clients.ReadOpts) (*ResolverMap, error) + Write(resource *ResolverMap, opts clients.WriteOpts) (*ResolverMap, error) + Delete(namespace, name string, opts clients.DeleteOpts) error + List(namespace string, opts clients.ListOpts) (ResolverMapList, error) + Watch(namespace string, opts clients.WatchOpts) (<-chan ResolverMapList, <-chan error, error) +} + +type resolverMapClient struct { + rc clients.ResourceClient +} + +func NewResolverMapClient(rcFactory factory.ResourceClientFactory) (ResolverMapClient, error) { + return NewResolverMapClientWithToken(rcFactory, "") +} + +func NewResolverMapClientWithToken(rcFactory factory.ResourceClientFactory, token string) (ResolverMapClient, error) { + rc, err := rcFactory.NewResourceClient(factory.NewResourceClientParams{ + ResourceType: &ResolverMap{}, + Token: token, + }) + if err != nil { + return nil, errors.Wrapf(err, "creating base ResolverMap resource client") + } + return NewResolverMapClientWithBase(rc), nil +} + +func NewResolverMapClientWithBase(rc clients.ResourceClient) ResolverMapClient { + return &resolverMapClient{ + rc: rc, + } +} + +func (client *resolverMapClient) BaseClient() clients.ResourceClient { + return client.rc +} + +func (client *resolverMapClient) Register() error { + return client.rc.Register() +} + +func (client *resolverMapClient) Read(namespace, name string, opts clients.ReadOpts) (*ResolverMap, error) { + opts = opts.WithDefaults() + + resource, err := client.rc.Read(namespace, name, opts) + if err != nil { + return nil, err + } + return resource.(*ResolverMap), nil +} + +func (client *resolverMapClient) Write(resolverMap *ResolverMap, opts clients.WriteOpts) (*ResolverMap, error) { + opts = opts.WithDefaults() + resource, err := client.rc.Write(resolverMap, opts) + if err != nil { + return nil, err + } + return resource.(*ResolverMap), nil +} + +func (client *resolverMapClient) Delete(namespace, name string, opts clients.DeleteOpts) error { + opts = opts.WithDefaults() + + return client.rc.Delete(namespace, name, opts) +} + +func (client *resolverMapClient) List(namespace string, opts clients.ListOpts) (ResolverMapList, error) { + opts = opts.WithDefaults() + + resourceList, err := client.rc.List(namespace, opts) + if err != nil { + return nil, err + } + return convertToResolverMap(resourceList), nil +} + +func (client *resolverMapClient) Watch(namespace string, opts clients.WatchOpts) (<-chan ResolverMapList, <-chan error, error) { + opts = opts.WithDefaults() + + resourcesChan, errs, initErr := client.rc.Watch(namespace, opts) + if initErr != nil { + return nil, nil, initErr + } + resolverMapsChan := make(chan ResolverMapList) + go func() { + for { + select { + case resourceList := <-resourcesChan: + resolverMapsChan <- convertToResolverMap(resourceList) + case <-opts.Ctx.Done(): + close(resolverMapsChan) + return + } + } + }() + return resolverMapsChan, errs, nil +} + +func convertToResolverMap(resources resources.ResourceList) ResolverMapList { + var resolverMapList ResolverMapList + for _, resource := range resources { + resolverMapList = append(resolverMapList, resource.(*ResolverMap)) + } + return resolverMapList +} diff --git a/pkg/api/v1/resolver_map_client_test.go b/pkg/api/v1/resolver_map_client_test.go new file mode 100644 index 0000000..00889b1 --- /dev/null +++ b/pkg/api/v1/resolver_map_client_test.go @@ -0,0 +1,181 @@ +// Code generated by solo-kit. DO NOT EDIT. + +// +build solokit + +package v1 + +import ( + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/solo-io/solo-kit/pkg/api/v1/clients" + "github.com/solo-io/solo-kit/pkg/api/v1/resources" + "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" + "github.com/solo-io/solo-kit/pkg/errors" + "github.com/solo-io/solo-kit/test/helpers" + "github.com/solo-io/solo-kit/test/tests/typed" +) + +var _ = Describe("ResolverMapClient", func() { + var ( + namespace string + ) + for _, test := range []typed.ResourceClientTester{ + &typed.KubeRcTester{Crd: ResolverMapCrd}, + &typed.ConsulRcTester{}, + &typed.FileRcTester{}, + &typed.MemoryRcTester{}, + &typed.VaultRcTester{}, + &typed.KubeSecretRcTester{}, + &typed.KubeConfigMapRcTester{}, + } { + Context("resource client backed by "+test.Description(), func() { + var ( + client ResolverMapClient + err error + name1, name2, name3 = "foo" + helpers.RandString(3), "boo" + helpers.RandString(3), "goo" + helpers.RandString(3) + ) + + BeforeEach(func() { + namespace = helpers.RandString(6) + factory := test.Setup(namespace) + client, err = NewResolverMapClient(factory) + Expect(err).NotTo(HaveOccurred()) + }) + AfterEach(func() { + test.Teardown(namespace) + }) + It("CRUDs ResolverMaps "+test.Description(), func() { + ResolverMapClientTest(namespace, client, name1, name2, name3) + }) + }) + } +}) + +func ResolverMapClientTest(namespace string, client ResolverMapClient, name1, name2, name3 string) { + err := client.Register() + Expect(err).NotTo(HaveOccurred()) + + name := name1 + input := NewResolverMap(namespace, name) + input.Metadata.Namespace = namespace + r1, err := client.Write(input, clients.WriteOpts{}) + Expect(err).NotTo(HaveOccurred()) + + _, err = client.Write(input, clients.WriteOpts{}) + Expect(err).To(HaveOccurred()) + Expect(errors.IsExist(err)).To(BeTrue()) + + Expect(r1).To(BeAssignableToTypeOf(&ResolverMap{})) + Expect(r1.GetMetadata().Name).To(Equal(name)) + Expect(r1.GetMetadata().Namespace).To(Equal(namespace)) + Expect(r1.Metadata.ResourceVersion).NotTo(Equal(input.Metadata.ResourceVersion)) + Expect(r1.Metadata.Ref()).To(Equal(input.Metadata.Ref())) + Expect(r1.Types).To(Equal(input.Types)) + Expect(r1.Status).To(Equal(input.Status)) + + _, err = client.Write(input, clients.WriteOpts{ + OverwriteExisting: true, + }) + Expect(err).To(HaveOccurred()) + + input.Metadata.ResourceVersion = r1.GetMetadata().ResourceVersion + r1, err = client.Write(input, clients.WriteOpts{ + OverwriteExisting: true, + }) + Expect(err).NotTo(HaveOccurred()) + read, err := client.Read(namespace, name, clients.ReadOpts{}) + Expect(err).NotTo(HaveOccurred()) + Expect(read).To(Equal(r1)) + _, err = client.Read("doesntexist", name, clients.ReadOpts{}) + Expect(err).To(HaveOccurred()) + Expect(errors.IsNotExist(err)).To(BeTrue()) + + name = name2 + input = &ResolverMap{} + + input.Metadata = core.Metadata{ + Name: name, + Namespace: namespace, + } + + r2, err := client.Write(input, clients.WriteOpts{}) + Expect(err).NotTo(HaveOccurred()) + list, err := client.List(namespace, clients.ListOpts{}) + Expect(err).NotTo(HaveOccurred()) + Expect(list).To(ContainElement(r1)) + Expect(list).To(ContainElement(r2)) + err = client.Delete(namespace, "adsfw", clients.DeleteOpts{}) + Expect(err).To(HaveOccurred()) + Expect(errors.IsNotExist(err)).To(BeTrue()) + err = client.Delete(namespace, "adsfw", clients.DeleteOpts{ + IgnoreNotExist: true, + }) + Expect(err).NotTo(HaveOccurred()) + err = client.Delete(namespace, r2.GetMetadata().Name, clients.DeleteOpts{}) + Expect(err).NotTo(HaveOccurred()) + + Eventually(func() ResolverMapList { + list, err = client.List(namespace, clients.ListOpts{}) + Expect(err).NotTo(HaveOccurred()) + return list + }, time.Second*10).Should(ContainElement(r1)) + Eventually(func() ResolverMapList { + list, err = client.List(namespace, clients.ListOpts{}) + Expect(err).NotTo(HaveOccurred()) + return list + }, time.Second*10).ShouldNot(ContainElement(r2)) + w, errs, err := client.Watch(namespace, clients.WatchOpts{ + RefreshRate: time.Hour, + }) + Expect(err).NotTo(HaveOccurred()) + + var r3 resources.Resource + wait := make(chan struct{}) + go func() { + defer close(wait) + defer GinkgoRecover() + + resources.UpdateMetadata(r2, func(meta *core.Metadata) { + meta.ResourceVersion = "" + }) + r2, err = client.Write(r2, clients.WriteOpts{}) + Expect(err).NotTo(HaveOccurred()) + + name = name3 + input = &ResolverMap{} + Expect(err).NotTo(HaveOccurred()) + input.Metadata = core.Metadata{ + Name: name, + Namespace: namespace, + } + + r3, err = client.Write(input, clients.WriteOpts{}) + Expect(err).NotTo(HaveOccurred()) + }() + <-wait + + select { + case err := <-errs: + Expect(err).NotTo(HaveOccurred()) + case list = <-w: + case <-time.After(time.Millisecond * 5): + Fail("expected a message in channel") + } + +drain: + for { + select { + case list = <-w: + case err := <-errs: + Expect(err).NotTo(HaveOccurred()) + case <-time.After(time.Millisecond * 500): + break drain + } + } + + Expect(list).To(ContainElement(r1)) + Expect(list).To(ContainElement(r2)) + Expect(list).To(ContainElement(r3)) +} diff --git a/pkg/api/v1/resolver_map_reconciler.sk.go b/pkg/api/v1/resolver_map_reconciler.sk.go new file mode 100644 index 0000000..1ce2307 --- /dev/null +++ b/pkg/api/v1/resolver_map_reconciler.sk.go @@ -0,0 +1,47 @@ +// Code generated by solo-kit. DO NOT EDIT. + +package v1 + +import ( + "github.com/solo-io/solo-kit/pkg/api/v1/clients" + "github.com/solo-io/solo-kit/pkg/api/v1/reconcile" + "github.com/solo-io/solo-kit/pkg/api/v1/resources" + "github.com/solo-io/solo-kit/pkg/utils/contextutils" +) + +// Option to copy anything from the original to the desired before writing. Return value of false means don't update +type TransitionResolverMapFunc func(original, desired *ResolverMap) (bool, error) + +type ResolverMapReconciler interface { + Reconcile(namespace string, desiredResources ResolverMapList, transition TransitionResolverMapFunc, opts clients.ListOpts) error +} + +func resolverMapsToResources(list ResolverMapList) resources.ResourceList { + var resourceList resources.ResourceList + for _, resolverMap := range list { + resourceList = append(resourceList, resolverMap) + } + return resourceList +} + +func NewResolverMapReconciler(client ResolverMapClient) ResolverMapReconciler { + return &resolverMapReconciler{ + base: reconcile.NewReconciler(client.BaseClient()), + } +} + +type resolverMapReconciler struct { + base reconcile.Reconciler +} + +func (r *resolverMapReconciler) Reconcile(namespace string, desiredResources ResolverMapList, transition TransitionResolverMapFunc, opts clients.ListOpts) error { + opts = opts.WithDefaults() + opts.Ctx = contextutils.WithLogger(opts.Ctx, "resolverMap_reconciler") + var transitionResources reconcile.TransitionResourcesFunc + if transition != nil { + transitionResources = func(original, desired resources.Resource) (bool, error) { + return transition(original.(*ResolverMap), desired.(*ResolverMap)) + } + } + return r.base.Reconcile(namespace, resolverMapsToResources(desiredResources), transitionResources, opts) +} diff --git a/pkg/api/v1/schema.pb.go b/pkg/api/v1/schema.pb.go new file mode 100644 index 0000000..fa01973 --- /dev/null +++ b/pkg/api/v1/schema.pb.go @@ -0,0 +1,153 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: github.com/solo-io/sqoop/api/v1/schema.proto + +package v1 + +import ( + bytes "bytes" + fmt "fmt" + math "math" + + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + core "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +// +// +// The Schema object wraps the user's GraphQL Schema, which is stored as an inline string. +// The Schema Object contains a Status field which is used by SQooP to validate the user's input schema. +// +// Schemas are matched to resolver maps in the same namespace with the same name +type Schema struct { + // inline the entire graphql schema as a string here + InlineSchema string `protobuf:"bytes,3,opt,name=inline_schema,json=inlineSchema,proto3" json:"inline_schema,omitempty"` + // Status indicates the validation status of this resource. + // Status is read-only by clients, and set by gloo during validation + Status core.Status `protobuf:"bytes,6,opt,name=status,proto3" json:"status" testdiff:"ignore"` + // Metadata contains the object metadata for this resource + Metadata core.Metadata `protobuf:"bytes,7,opt,name=metadata,proto3" json:"metadata"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Schema) Reset() { *m = Schema{} } +func (m *Schema) String() string { return proto.CompactTextString(m) } +func (*Schema) ProtoMessage() {} +func (*Schema) Descriptor() ([]byte, []int) { + return fileDescriptor_1bad29d19fe9e698, []int{0} +} +func (m *Schema) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Schema.Unmarshal(m, b) +} +func (m *Schema) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Schema.Marshal(b, m, deterministic) +} +func (m *Schema) XXX_Merge(src proto.Message) { + xxx_messageInfo_Schema.Merge(m, src) +} +func (m *Schema) XXX_Size() int { + return xxx_messageInfo_Schema.Size(m) +} +func (m *Schema) XXX_DiscardUnknown() { + xxx_messageInfo_Schema.DiscardUnknown(m) +} + +var xxx_messageInfo_Schema proto.InternalMessageInfo + +func (m *Schema) GetInlineSchema() string { + if m != nil { + return m.InlineSchema + } + return "" +} + +func (m *Schema) GetStatus() core.Status { + if m != nil { + return m.Status + } + return core.Status{} +} + +func (m *Schema) GetMetadata() core.Metadata { + if m != nil { + return m.Metadata + } + return core.Metadata{} +} + +func init() { + proto.RegisterType((*Schema)(nil), "sqoop.solo.io.Schema") +} + +func init() { + proto.RegisterFile("github.com/solo-io/sqoop/api/v1/schema.proto", fileDescriptor_1bad29d19fe9e698) +} + +var fileDescriptor_1bad29d19fe9e698 = []byte{ + // 282 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x49, 0xcf, 0x2c, 0xc9, + 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x2f, 0xce, 0xcf, 0xc9, 0xd7, 0xcd, 0xcc, 0xd7, 0x2f, + 0x2e, 0xcc, 0xcf, 0x2f, 0xd0, 0x4f, 0x2c, 0xc8, 0xd4, 0x2f, 0x33, 0xd4, 0x2f, 0x4e, 0xce, 0x48, + 0xcd, 0x4d, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x05, 0x4b, 0xe9, 0x81, 0x14, 0xea, + 0x65, 0xe6, 0x4b, 0x89, 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0x65, 0xf4, 0x41, 0x2c, 0x88, 0x22, 0x29, + 0x43, 0x6c, 0x46, 0x82, 0xe8, 0xec, 0xcc, 0x12, 0x98, 0xa9, 0xb9, 0xa9, 0x25, 0x89, 0x29, 0x89, + 0x25, 0x50, 0x73, 0xa5, 0xf4, 0x89, 0xd0, 0x52, 0x5c, 0x92, 0x58, 0x52, 0x5a, 0x4c, 0x82, 0x1d, + 0x30, 0x3e, 0x44, 0x8b, 0xd2, 0x71, 0x46, 0x2e, 0xb6, 0x60, 0xb0, 0x67, 0x84, 0x94, 0xb9, 0x78, + 0x33, 0xf3, 0x72, 0x32, 0xf3, 0x52, 0xe3, 0x21, 0xbe, 0x93, 0x60, 0x56, 0x60, 0xd4, 0xe0, 0x0c, + 0xe2, 0x81, 0x08, 0x42, 0x15, 0xb9, 0x73, 0xb1, 0x41, 0xac, 0x94, 0x60, 0x53, 0x60, 0xd4, 0xe0, + 0x36, 0x12, 0xd1, 0x4b, 0xce, 0x2f, 0x4a, 0x85, 0xf9, 0x5d, 0x2f, 0x18, 0x2c, 0xe7, 0x24, 0x79, + 0xe2, 0x9e, 0x3c, 0xc3, 0xa7, 0x7b, 0xf2, 0x82, 0x25, 0xa9, 0xc5, 0x25, 0x29, 0x99, 0x69, 0x69, + 0x56, 0x4a, 0x99, 0xe9, 0x79, 0xf9, 0x45, 0xa9, 0x4a, 0x41, 0x50, 0xed, 0x42, 0x16, 0x5c, 0x1c, + 0x30, 0xef, 0x4a, 0xb0, 0x83, 0x8d, 0x12, 0x43, 0x35, 0xca, 0x17, 0x2a, 0xeb, 0xc4, 0x02, 0x32, + 0x2c, 0x08, 0xae, 0xda, 0x4a, 0xb4, 0xe9, 0x23, 0x0b, 0x0b, 0x17, 0x53, 0x71, 0x72, 0xd3, 0x47, + 0x16, 0x4e, 0x21, 0x76, 0x88, 0x63, 0x8b, 0x9d, 0x34, 0x57, 0x3c, 0x92, 0x63, 0x8c, 0x52, 0xc6, + 0x19, 0x73, 0x05, 0xd9, 0xe9, 0xd0, 0x30, 0x48, 0x62, 0x03, 0xfb, 0xdd, 0x18, 0x10, 0x00, 0x00, + 0xff, 0xff, 0xe1, 0x56, 0xec, 0x7d, 0xe7, 0x01, 0x00, 0x00, +} + +func (this *Schema) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Schema) + if !ok { + that2, ok := that.(Schema) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.InlineSchema != that1.InlineSchema { + return false + } + if !this.Status.Equal(&that1.Status) { + return false + } + if !this.Metadata.Equal(&that1.Metadata) { + return false + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } + return true +} diff --git a/pkg/api/v1/schema.sk.go b/pkg/api/v1/schema.sk.go new file mode 100644 index 0000000..04c736b --- /dev/null +++ b/pkg/api/v1/schema.sk.go @@ -0,0 +1,167 @@ +// Code generated by solo-kit. DO NOT EDIT. + +package v1 + +import ( + "sort" + + "github.com/gogo/protobuf/proto" + "github.com/solo-io/solo-kit/pkg/api/v1/clients/kube/crd" + "github.com/solo-io/solo-kit/pkg/api/v1/resources" + "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" + "github.com/solo-io/solo-kit/pkg/errors" + "github.com/solo-io/solo-kit/pkg/utils/hashutils" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// TODO: modify as needed to populate additional fields +func NewSchema(namespace, name string) *Schema { + return &Schema{ + Metadata: core.Metadata{ + Name: name, + Namespace: namespace, + }, + } +} + +func (r *Schema) SetStatus(status core.Status) { + r.Status = status +} + +func (r *Schema) SetMetadata(meta core.Metadata) { + r.Metadata = meta +} + +func (r *Schema) Hash() uint64 { + metaCopy := r.GetMetadata() + metaCopy.ResourceVersion = "" + return hashutils.HashAll( + metaCopy, + r.InlineSchema, + ) +} + +type SchemaList []*Schema +type SchemasByNamespace map[string]SchemaList + +// namespace is optional, if left empty, names can collide if the list contains more than one with the same name +func (list SchemaList) Find(namespace, name string) (*Schema, error) { + for _, schema := range list { + if schema.Metadata.Name == name { + if namespace == "" || schema.Metadata.Namespace == namespace { + return schema, nil + } + } + } + return nil, errors.Errorf("list did not find schema %v.%v", namespace, name) +} + +func (list SchemaList) AsResources() resources.ResourceList { + var ress resources.ResourceList + for _, schema := range list { + ress = append(ress, schema) + } + return ress +} + +func (list SchemaList) AsInputResources() resources.InputResourceList { + var ress resources.InputResourceList + for _, schema := range list { + ress = append(ress, schema) + } + return ress +} + +func (list SchemaList) Names() []string { + var names []string + for _, schema := range list { + names = append(names, schema.Metadata.Name) + } + return names +} + +func (list SchemaList) NamespacesDotNames() []string { + var names []string + for _, schema := range list { + names = append(names, schema.Metadata.Namespace+"."+schema.Metadata.Name) + } + return names +} + +func (list SchemaList) Sort() SchemaList { + sort.SliceStable(list, func(i, j int) bool { + return list[i].Metadata.Less(list[j].Metadata) + }) + return list +} + +func (list SchemaList) Clone() SchemaList { + var schemaList SchemaList + for _, schema := range list { + schemaList = append(schemaList, proto.Clone(schema).(*Schema)) + } + return schemaList +} + +func (list SchemaList) Each(f func(element *Schema)) { + for _, schema := range list { + f(schema) + } +} + +func (list SchemaList) AsInterfaces() []interface{} { + var asInterfaces []interface{} + list.Each(func(element *Schema) { + asInterfaces = append(asInterfaces, element) + }) + return asInterfaces +} + +func (byNamespace SchemasByNamespace) Add(schema ...*Schema) { + for _, item := range schema { + byNamespace[item.Metadata.Namespace] = append(byNamespace[item.Metadata.Namespace], item) + } +} + +func (byNamespace SchemasByNamespace) Clear(namespace string) { + delete(byNamespace, namespace) +} + +func (byNamespace SchemasByNamespace) List() SchemaList { + var list SchemaList + for _, schemaList := range byNamespace { + list = append(list, schemaList...) + } + return list.Sort() +} + +func (byNamespace SchemasByNamespace) Clone() SchemasByNamespace { + cloned := make(SchemasByNamespace) + for ns, list := range byNamespace { + cloned[ns] = list.Clone() + } + return cloned +} + +var _ resources.Resource = &Schema{} + +// Kubernetes Adapter for Schema + +func (o *Schema) GetObjectKind() schema.ObjectKind { + t := SchemaCrd.TypeMeta() + return &t +} + +func (o *Schema) DeepCopyObject() runtime.Object { + return resources.Clone(o).(*Schema) +} + +var SchemaCrd = crd.NewCrd("sqoop.solo.io", + "schemas", + "sqoop.solo.io", + "v1", + "Schema", + "sc", + false, + &Schema{}) diff --git a/pkg/api/v1/schema_client.sk.go b/pkg/api/v1/schema_client.sk.go new file mode 100644 index 0000000..fea1d90 --- /dev/null +++ b/pkg/api/v1/schema_client.sk.go @@ -0,0 +1,118 @@ +// Code generated by solo-kit. DO NOT EDIT. + +package v1 + +import ( + "github.com/solo-io/solo-kit/pkg/api/v1/clients" + "github.com/solo-io/solo-kit/pkg/api/v1/clients/factory" + "github.com/solo-io/solo-kit/pkg/api/v1/resources" + "github.com/solo-io/solo-kit/pkg/errors" +) + +type SchemaClient interface { + BaseClient() clients.ResourceClient + Register() error + Read(namespace, name string, opts clients.ReadOpts) (*Schema, error) + Write(resource *Schema, opts clients.WriteOpts) (*Schema, error) + Delete(namespace, name string, opts clients.DeleteOpts) error + List(namespace string, opts clients.ListOpts) (SchemaList, error) + Watch(namespace string, opts clients.WatchOpts) (<-chan SchemaList, <-chan error, error) +} + +type schemaClient struct { + rc clients.ResourceClient +} + +func NewSchemaClient(rcFactory factory.ResourceClientFactory) (SchemaClient, error) { + return NewSchemaClientWithToken(rcFactory, "") +} + +func NewSchemaClientWithToken(rcFactory factory.ResourceClientFactory, token string) (SchemaClient, error) { + rc, err := rcFactory.NewResourceClient(factory.NewResourceClientParams{ + ResourceType: &Schema{}, + Token: token, + }) + if err != nil { + return nil, errors.Wrapf(err, "creating base Schema resource client") + } + return NewSchemaClientWithBase(rc), nil +} + +func NewSchemaClientWithBase(rc clients.ResourceClient) SchemaClient { + return &schemaClient{ + rc: rc, + } +} + +func (client *schemaClient) BaseClient() clients.ResourceClient { + return client.rc +} + +func (client *schemaClient) Register() error { + return client.rc.Register() +} + +func (client *schemaClient) Read(namespace, name string, opts clients.ReadOpts) (*Schema, error) { + opts = opts.WithDefaults() + + resource, err := client.rc.Read(namespace, name, opts) + if err != nil { + return nil, err + } + return resource.(*Schema), nil +} + +func (client *schemaClient) Write(schema *Schema, opts clients.WriteOpts) (*Schema, error) { + opts = opts.WithDefaults() + resource, err := client.rc.Write(schema, opts) + if err != nil { + return nil, err + } + return resource.(*Schema), nil +} + +func (client *schemaClient) Delete(namespace, name string, opts clients.DeleteOpts) error { + opts = opts.WithDefaults() + + return client.rc.Delete(namespace, name, opts) +} + +func (client *schemaClient) List(namespace string, opts clients.ListOpts) (SchemaList, error) { + opts = opts.WithDefaults() + + resourceList, err := client.rc.List(namespace, opts) + if err != nil { + return nil, err + } + return convertToSchema(resourceList), nil +} + +func (client *schemaClient) Watch(namespace string, opts clients.WatchOpts) (<-chan SchemaList, <-chan error, error) { + opts = opts.WithDefaults() + + resourcesChan, errs, initErr := client.rc.Watch(namespace, opts) + if initErr != nil { + return nil, nil, initErr + } + schemasChan := make(chan SchemaList) + go func() { + for { + select { + case resourceList := <-resourcesChan: + schemasChan <- convertToSchema(resourceList) + case <-opts.Ctx.Done(): + close(schemasChan) + return + } + } + }() + return schemasChan, errs, nil +} + +func convertToSchema(resources resources.ResourceList) SchemaList { + var schemaList SchemaList + for _, resource := range resources { + schemaList = append(schemaList, resource.(*Schema)) + } + return schemaList +} diff --git a/pkg/api/v1/schema_client_test.go b/pkg/api/v1/schema_client_test.go new file mode 100644 index 0000000..222a453 --- /dev/null +++ b/pkg/api/v1/schema_client_test.go @@ -0,0 +1,181 @@ +// Code generated by solo-kit. DO NOT EDIT. + +// +build solokit + +package v1 + +import ( + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/solo-io/solo-kit/pkg/api/v1/clients" + "github.com/solo-io/solo-kit/pkg/api/v1/resources" + "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" + "github.com/solo-io/solo-kit/pkg/errors" + "github.com/solo-io/solo-kit/test/helpers" + "github.com/solo-io/solo-kit/test/tests/typed" +) + +var _ = Describe("SchemaClient", func() { + var ( + namespace string + ) + for _, test := range []typed.ResourceClientTester{ + &typed.KubeRcTester{Crd: SchemaCrd}, + &typed.ConsulRcTester{}, + &typed.FileRcTester{}, + &typed.MemoryRcTester{}, + &typed.VaultRcTester{}, + &typed.KubeSecretRcTester{}, + &typed.KubeConfigMapRcTester{}, + } { + Context("resource client backed by "+test.Description(), func() { + var ( + client SchemaClient + err error + name1, name2, name3 = "foo" + helpers.RandString(3), "boo" + helpers.RandString(3), "goo" + helpers.RandString(3) + ) + + BeforeEach(func() { + namespace = helpers.RandString(6) + factory := test.Setup(namespace) + client, err = NewSchemaClient(factory) + Expect(err).NotTo(HaveOccurred()) + }) + AfterEach(func() { + test.Teardown(namespace) + }) + It("CRUDs Schemas "+test.Description(), func() { + SchemaClientTest(namespace, client, name1, name2, name3) + }) + }) + } +}) + +func SchemaClientTest(namespace string, client SchemaClient, name1, name2, name3 string) { + err := client.Register() + Expect(err).NotTo(HaveOccurred()) + + name := name1 + input := NewSchema(namespace, name) + input.Metadata.Namespace = namespace + r1, err := client.Write(input, clients.WriteOpts{}) + Expect(err).NotTo(HaveOccurred()) + + _, err = client.Write(input, clients.WriteOpts{}) + Expect(err).To(HaveOccurred()) + Expect(errors.IsExist(err)).To(BeTrue()) + + Expect(r1).To(BeAssignableToTypeOf(&Schema{})) + Expect(r1.GetMetadata().Name).To(Equal(name)) + Expect(r1.GetMetadata().Namespace).To(Equal(namespace)) + Expect(r1.Metadata.ResourceVersion).NotTo(Equal(input.Metadata.ResourceVersion)) + Expect(r1.Metadata.Ref()).To(Equal(input.Metadata.Ref())) + Expect(r1.InlineSchema).To(Equal(input.InlineSchema)) + Expect(r1.Status).To(Equal(input.Status)) + + _, err = client.Write(input, clients.WriteOpts{ + OverwriteExisting: true, + }) + Expect(err).To(HaveOccurred()) + + input.Metadata.ResourceVersion = r1.GetMetadata().ResourceVersion + r1, err = client.Write(input, clients.WriteOpts{ + OverwriteExisting: true, + }) + Expect(err).NotTo(HaveOccurred()) + read, err := client.Read(namespace, name, clients.ReadOpts{}) + Expect(err).NotTo(HaveOccurred()) + Expect(read).To(Equal(r1)) + _, err = client.Read("doesntexist", name, clients.ReadOpts{}) + Expect(err).To(HaveOccurred()) + Expect(errors.IsNotExist(err)).To(BeTrue()) + + name = name2 + input = &Schema{} + + input.Metadata = core.Metadata{ + Name: name, + Namespace: namespace, + } + + r2, err := client.Write(input, clients.WriteOpts{}) + Expect(err).NotTo(HaveOccurred()) + list, err := client.List(namespace, clients.ListOpts{}) + Expect(err).NotTo(HaveOccurred()) + Expect(list).To(ContainElement(r1)) + Expect(list).To(ContainElement(r2)) + err = client.Delete(namespace, "adsfw", clients.DeleteOpts{}) + Expect(err).To(HaveOccurred()) + Expect(errors.IsNotExist(err)).To(BeTrue()) + err = client.Delete(namespace, "adsfw", clients.DeleteOpts{ + IgnoreNotExist: true, + }) + Expect(err).NotTo(HaveOccurred()) + err = client.Delete(namespace, r2.GetMetadata().Name, clients.DeleteOpts{}) + Expect(err).NotTo(HaveOccurred()) + + Eventually(func() SchemaList { + list, err = client.List(namespace, clients.ListOpts{}) + Expect(err).NotTo(HaveOccurred()) + return list + }, time.Second*10).Should(ContainElement(r1)) + Eventually(func() SchemaList { + list, err = client.List(namespace, clients.ListOpts{}) + Expect(err).NotTo(HaveOccurred()) + return list + }, time.Second*10).ShouldNot(ContainElement(r2)) + w, errs, err := client.Watch(namespace, clients.WatchOpts{ + RefreshRate: time.Hour, + }) + Expect(err).NotTo(HaveOccurred()) + + var r3 resources.Resource + wait := make(chan struct{}) + go func() { + defer close(wait) + defer GinkgoRecover() + + resources.UpdateMetadata(r2, func(meta *core.Metadata) { + meta.ResourceVersion = "" + }) + r2, err = client.Write(r2, clients.WriteOpts{}) + Expect(err).NotTo(HaveOccurred()) + + name = name3 + input = &Schema{} + Expect(err).NotTo(HaveOccurred()) + input.Metadata = core.Metadata{ + Name: name, + Namespace: namespace, + } + + r3, err = client.Write(input, clients.WriteOpts{}) + Expect(err).NotTo(HaveOccurred()) + }() + <-wait + + select { + case err := <-errs: + Expect(err).NotTo(HaveOccurred()) + case list = <-w: + case <-time.After(time.Millisecond * 5): + Fail("expected a message in channel") + } + +drain: + for { + select { + case list = <-w: + case err := <-errs: + Expect(err).NotTo(HaveOccurred()) + case <-time.After(time.Millisecond * 500): + break drain + } + } + + Expect(list).To(ContainElement(r1)) + Expect(list).To(ContainElement(r2)) + Expect(list).To(ContainElement(r3)) +} diff --git a/pkg/api/v1/schema_reconciler.sk.go b/pkg/api/v1/schema_reconciler.sk.go new file mode 100644 index 0000000..387a806 --- /dev/null +++ b/pkg/api/v1/schema_reconciler.sk.go @@ -0,0 +1,47 @@ +// Code generated by solo-kit. DO NOT EDIT. + +package v1 + +import ( + "github.com/solo-io/solo-kit/pkg/api/v1/clients" + "github.com/solo-io/solo-kit/pkg/api/v1/reconcile" + "github.com/solo-io/solo-kit/pkg/api/v1/resources" + "github.com/solo-io/solo-kit/pkg/utils/contextutils" +) + +// Option to copy anything from the original to the desired before writing. Return value of false means don't update +type TransitionSchemaFunc func(original, desired *Schema) (bool, error) + +type SchemaReconciler interface { + Reconcile(namespace string, desiredResources SchemaList, transition TransitionSchemaFunc, opts clients.ListOpts) error +} + +func schemasToResources(list SchemaList) resources.ResourceList { + var resourceList resources.ResourceList + for _, schema := range list { + resourceList = append(resourceList, schema) + } + return resourceList +} + +func NewSchemaReconciler(client SchemaClient) SchemaReconciler { + return &schemaReconciler{ + base: reconcile.NewReconciler(client.BaseClient()), + } +} + +type schemaReconciler struct { + base reconcile.Reconciler +} + +func (r *schemaReconciler) Reconcile(namespace string, desiredResources SchemaList, transition TransitionSchemaFunc, opts clients.ListOpts) error { + opts = opts.WithDefaults() + opts.Ctx = contextutils.WithLogger(opts.Ctx, "schema_reconciler") + var transitionResources reconcile.TransitionResourcesFunc + if transition != nil { + transitionResources = func(original, desired resources.Resource) (bool, error) { + return transition(original.(*Schema), desired.(*Schema)) + } + } + return r.base.Reconcile(namespace, schemasToResources(desiredResources), transitionResources, opts) +} diff --git a/pkg/api/v1/sqoop.solo.io_suite_test.go b/pkg/api/v1/sqoop.solo.io_suite_test.go new file mode 100644 index 0000000..4c7cc5d --- /dev/null +++ b/pkg/api/v1/sqoop.solo.io_suite_test.go @@ -0,0 +1,15 @@ +// Code generated by solo-kit. DO NOT EDIT. + +package v1 + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestSqoopsoloio(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Sqoopsoloio Suite") +} diff --git a/pkg/bootstrap/bootstrap.go b/pkg/bootstrap/bootstrap.go deleted file mode 100644 index 38eb98f..0000000 --- a/pkg/bootstrap/bootstrap.go +++ /dev/null @@ -1,52 +0,0 @@ -package bootstrap - -import ( - "github.com/pkg/errors" - "github.com/solo-io/gloo/pkg/bootstrap" - "github.com/solo-io/sqoop/pkg/storage" - "github.com/solo-io/sqoop/pkg/storage/consul" - "github.com/solo-io/sqoop/pkg/storage/crd" - "github.com/solo-io/sqoop/pkg/storage/file" - "k8s.io/client-go/tools/clientcmd" -) - -type Options struct { - bootstrap.Options - VirtualServiceName string - RoleName string - ProxyAddr string - BindAddr string -} - -func Bootstrap(opts bootstrap.Options) (storage.Interface, error) { - switch opts.ConfigStorageOptions.Type { - case bootstrap.WatcherTypeFile: - dir := opts.FileOptions.ConfigDir - if dir == "" { - return nil, errors.New("must provide directory for file config watcher") - } - client, err := file.NewStorage(dir, opts.ConfigStorageOptions.SyncFrequency) - if err != nil { - return nil, errors.Wrapf(err, "failed to start file config watcher for directory %v", dir) - } - return client, nil - case bootstrap.WatcherTypeKube: - cfg, err := clientcmd.BuildConfigFromFlags(opts.KubeOptions.MasterURL, opts.KubeOptions.KubeConfig) - if err != nil { - return nil, errors.Wrap(err, "building kube restclient") - } - cfgWatcher, err := crd.NewStorage(cfg, opts.KubeOptions.Namespace, opts.ConfigStorageOptions.SyncFrequency) - if err != nil { - return nil, errors.Wrapf(err, "failed to start kube config watcher with config %#v", opts.KubeOptions) - } - return cfgWatcher, nil - case bootstrap.WatcherTypeConsul: - cfg := opts.ConsulOptions.ToConsulConfig() - cfgWatcher, err := consul.NewStorage(cfg, opts.ConsulOptions.RootPath, opts.ConfigStorageOptions.SyncFrequency) - if err != nil { - return nil, errors.Wrapf(err, "failed to start consul config watcher with config %#v", opts.ConsulOptions) - } - return cfgWatcher, nil - } - return nil, errors.Errorf("unknown or unspecified config watcher type: %v", opts.ConfigStorageOptions.Type) -} diff --git a/pkg/bootstrap/flags/sqoop_flags.go b/pkg/bootstrap/flags/sqoop_flags.go deleted file mode 100644 index bc5b2d1..0000000 --- a/pkg/bootstrap/flags/sqoop_flags.go +++ /dev/null @@ -1,18 +0,0 @@ -package flags - -import ( - "github.com/solo-io/sqoop/pkg/bootstrap" - "github.com/spf13/cobra" -) - -func AddSqoopFlags(cmd *cobra.Command, opts *bootstrap.Options) { - // TODO ingress.bind-adress - cmd.PersistentFlags().StringVar(&opts.VirtualServiceName, "sqoop.virtualservice", "sqoop-routes", "the "+ - "name of the virtual service Sqoop will use to store its routes") - cmd.PersistentFlags().StringVar(&opts.RoleName, "sqoop.role", "sqoop", "the "+ - "name of the mesh role to assign to Sqoop when communicating with Gloo") - cmd.PersistentFlags().StringVar(&opts.ProxyAddr, "sqoop.proxy-addr", "localhost:8080", "the "+ - "address (hostname:port) of the Sqoop proxy") - cmd.PersistentFlags().StringVar(&opts.BindAddr, "sqoop.bind-addr", ":9090", "the "+ - "address for the Sqoop server to listen on") -} diff --git a/pkg/configwatcher/config_watcher.go b/pkg/configwatcher/config_watcher.go deleted file mode 100644 index a1217b0..0000000 --- a/pkg/configwatcher/config_watcher.go +++ /dev/null @@ -1,136 +0,0 @@ -package configwatcher - -import ( - "fmt" - "sort" - "sync" - - "github.com/pkg/errors" - - "github.com/gogo/protobuf/proto" - "github.com/mitchellh/hashstructure" - "github.com/solo-io/gloo/pkg/log" - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/storage" -) - -type configWatcher struct { - watchers []*storage.Watcher - configs chan *v1.Config - errs chan error -} - -func NewConfigWatcher(storageClient storage.Interface) (*configWatcher, error) { - if err := storageClient.V1().Register(); err != nil && !storage.IsAlreadyExists(err) { - return nil, fmt.Errorf("failed to register to storage backend: %v", err) - } - - configs := make(chan *v1.Config, 1) - // do a first time read - cache := &v1.Config{ - Schemas: nil, - ResolverMaps: nil, - } - - syncSchemas := func(updatedList []*v1.Schema, _ *v1.Schema) { - sort.SliceStable(updatedList, func(i, j int) bool { - return updatedList[i].GetName() < updatedList[j].GetName() - }) - - oldHash, newHash := hashSchemas(cache.Schemas), hashSchemas(updatedList) - if oldHash == newHash { - return - } - log.GreyPrintf("\nold hash: %v\nnew hash: %v", oldHash, newHash) - - cache.Schemas = updatedList - configs <- proto.Clone(cache).(*v1.Config) - } - schemaWatcher, err := storageClient.V1().Schemas().Watch(&storage.SchemaEventHandlerFuncs{ - AddFunc: syncSchemas, - UpdateFunc: syncSchemas, - DeleteFunc: syncSchemas, - }) - if err != nil { - return nil, errors.Wrap(err, "failed to create watcher for schemas") - } - - syncResolverMaps := func(updatedList []*v1.ResolverMap, _ *v1.ResolverMap) { - sort.SliceStable(updatedList, func(i, j int) bool { - return updatedList[i].GetName() < updatedList[j].GetName() - }) - - oldHash, newHash := hashResolverMaps(cache.ResolverMaps), hashResolverMaps(updatedList) - if oldHash == newHash { - return - } - log.GreyPrintf("\nold hash: %v\nnew hash: %v", oldHash, newHash) - - cache.ResolverMaps = updatedList - configs <- proto.Clone(cache).(*v1.Config) - } - resolverMapWatcher, err := storageClient.V1().ResolverMaps().Watch(&storage.ResolverMapEventHandlerFuncs{ - AddFunc: syncResolverMaps, - UpdateFunc: syncResolverMaps, - DeleteFunc: syncResolverMaps, - }) - if err != nil { - return nil, errors.Wrap(err, "failed to create watcher for virtualservices") - } - - return &configWatcher{ - watchers: []*storage.Watcher{resolverMapWatcher, schemaWatcher}, - configs: configs, - errs: make(chan error), - }, nil -} - -func (w *configWatcher) Run(stop <-chan struct{}) { - done := &sync.WaitGroup{} - for _, watcher := range w.watchers { - done.Add(1) - go func(watcher *storage.Watcher, stop <-chan struct{}, errs chan error) { - watcher.Run(stop, errs) - done.Done() - }(watcher, stop, w.errs) - } - done.Wait() -} - -func (w *configWatcher) Config() <-chan *v1.Config { - return w.configs -} - -func (w *configWatcher) Error() <-chan error { - return w.errs -} - -func hashSchemas(schemas []*v1.Schema) uint64 { - // shave off status and resource version - for _, item := range schemas { - item.Status = nil - if item.Metadata != nil { - item.Metadata.ResourceVersion = "" - } - } - h, err := hashstructure.Hash(schemas, nil) - if err != nil { - panic(err) - } - return h -} - -func hashResolverMaps(resolverMaps []*v1.ResolverMap) uint64 { - // shave off status and resource version - for _, item := range resolverMaps { - item.Status = nil - if item.Metadata != nil { - item.Metadata.ResourceVersion = "" - } - } - h, err := hashstructure.Hash(resolverMaps, nil) - if err != nil { - panic(err) - } - return h -} diff --git a/pkg/configwatcher/configwatcher_suite_test.go b/pkg/configwatcher/configwatcher_suite_test.go deleted file mode 100644 index 9e4633b..0000000 --- a/pkg/configwatcher/configwatcher_suite_test.go +++ /dev/null @@ -1,15 +0,0 @@ -package configwatcher - -import ( - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "github.com/solo-io/gloo/pkg/log" -) - -func TestConfigwatcher(t *testing.T) { - RegisterFailHandler(Fail) - log.DefaultOut = GinkgoWriter - RunSpecs(t, "Configwatcher Suite") -} diff --git a/pkg/configwatcher/file_config_watcher_test.go b/pkg/configwatcher/file_config_watcher_test.go deleted file mode 100644 index aaca996..0000000 --- a/pkg/configwatcher/file_config_watcher_test.go +++ /dev/null @@ -1,70 +0,0 @@ -package configwatcher - -import ( - "fmt" - "io/ioutil" - "os" - "time" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - "github.com/solo-io/gloo/pkg/log" - . "github.com/solo-io/gloo/test/helpers" - "github.com/solo-io/sqoop/pkg/storage/file" - "github.com/solo-io/sqoop/test" -) - -var _ = Describe("FileConfigWatcher", func() { - var ( - dir string - err error - ) - BeforeEach(func() { - dir, err = ioutil.TempDir("", "filecachetest") - Must(err) - }) - AfterEach(func() { - log.Debugf("removing " + dir) - os.RemoveAll(dir) - }) - Describe("controller", func() { - It("watches gloo files", func() { - storageClient, err := file.NewStorage(dir, time.Millisecond) - Must(err) - watcher, err := NewConfigWatcher(storageClient) - Must(err) - go func() { watcher.Run(make(chan struct{})) }() - - time.Sleep(time.Second) - - schema := test.StarWarsV1Schema() - - created, err := storageClient.V1().Schemas().Create(schema) - Expect(err).NotTo(HaveOccurred()) - - select { - case <-time.After(time.Second * 5): - Expect(fmt.Errorf("expected to have received resource event before 5s")).NotTo(HaveOccurred()) - case cfg := <-watcher.Config(): - Expect(len(cfg.Schemas)).To(Equal(1)) - Expect(cfg.Schemas[0].InlineSchema).To(Equal(schema.InlineSchema)) - case err := <-watcher.Error(): - Expect(err).NotTo(HaveOccurred()) - } - - // update with no op, should not give us a config - _, err = storageClient.V1().Schemas().Update(created) - Expect(err).NotTo(HaveOccurred()) - - select { - case <-time.After(time.Millisecond * 50): - case c := <-watcher.Config(): - Expect(c).To(BeNil()) - Expect(fmt.Errorf("should not have recieved duplicate config")).NotTo(HaveOccurred()) - case err := <-watcher.Error(): - Expect(err).NotTo(HaveOccurred()) - } - }) - }) -}) diff --git a/pkg/configwatcher/interface.go b/pkg/configwatcher/interface.go deleted file mode 100644 index 4ba26f7..0000000 --- a/pkg/configwatcher/interface.go +++ /dev/null @@ -1,9 +0,0 @@ -package configwatcher - -import "github.com/solo-io/sqoop/pkg/api/types/v1" - -type Interface interface { - Run(stop <-chan struct{}) - Config() <-chan *v1.Config - Error() <-chan error -} diff --git a/pkg/core/core_suite_test.go b/pkg/core/core_suite_test.go deleted file mode 100644 index 4f4fe32..0000000 --- a/pkg/core/core_suite_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package core_test - -import ( - "testing" - - "github.com/solo-io/gloo/test/helpers/local" - - "fmt" - "net/http" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "github.com/solo-io/gloo/pkg/log" - "github.com/solo-io/gloo/test/helpers" - "github.com/solo-io/sqoop/examples/starwars/server" -) - -var ( - envoyFactory *localhelpers.EnvoyFactory - glooFactory *localhelpers.GlooFactory - starWarsRest *http.Server - starWarsPort uint32 -) - -func TestLocalE2e(t *testing.T) { - helpers.RegisterCommonFailHandlers() - log.DefaultOut = GinkgoWriter - RunSpecs(t, "LocalE2e Suite") -} - -var _ = BeforeSuite(func() { - var err error - envoyFactory, err = localhelpers.NewEnvoyFactory() - Expect(err).NotTo(HaveOccurred()) - glooFactory, err = localhelpers.NewGlooFactory() - Expect(err).NotTo(HaveOccurred()) - starWarsPort = 1234 - starWarsRest = &http.Server{ - Addr: fmt.Sprintf("localhost:%v", starWarsPort), - Handler: server.New(), - } -}) - -var _ = AfterSuite(func() { - envoyFactory.Clean() - glooFactory.Clean() -}) - -var ( - envoyInstance *localhelpers.EnvoyInstance - glooInstance *localhelpers.GlooInstance -) - -var _ = BeforeEach(func() { - var err error - envoyInstance, err = envoyFactory.NewEnvoyInstance() - Expect(err).NotTo(HaveOccurred()) - glooInstance, err = glooFactory.NewGlooInstance() - Expect(err).NotTo(HaveOccurred()) - go func() { - err := starWarsRest.ListenAndServe() - if err != nil { - log.Printf("starwars server error: %v", err.Error()) - } - }() -}) - -var _ = AfterEach(func() { - if envoyInstance != nil { - envoyInstance.Clean() - } - if glooInstance != nil { - glooInstance.Clean() - } - starWarsRest.Close() -}) diff --git a/pkg/core/core_test.go b/pkg/core/core_test.go deleted file mode 100644 index daa08ff..0000000 --- a/pkg/core/core_test.go +++ /dev/null @@ -1,169 +0,0 @@ -package core_test - -import ( - "bytes" - "fmt" - "io/ioutil" - "math/rand" - "net/http" - "time" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "github.com/pkg/errors" - gloov1 "github.com/solo-io/gloo/pkg/api/types/v1" - glooopts "github.com/solo-io/gloo/pkg/bootstrap" - "github.com/solo-io/gloo/pkg/bootstrap/configstorage" - "github.com/solo-io/gloo/pkg/coreplugins/static" - "github.com/solo-io/gloo/pkg/plugins/rest" - "github.com/solo-io/sqoop/pkg/bootstrap" - . "github.com/solo-io/sqoop/pkg/core" - "github.com/solo-io/sqoop/test" - "github.com/gogo/protobuf/types" -) - -var sqoopPort int - -var _ = Describe("Core", func() { - It("does the happy path", func() { - rand.Seed(time.Now().Unix()) - sqoopPort = 9090 - opts := bootstrap.Options{ - Options: glooopts.Options{ - ConfigStorageOptions: glooopts.StorageOptions{ - Type: "file", - SyncFrequency: time.Millisecond, - }, - FileOptions: glooopts.FileOptions{ - ConfigDir: glooInstance.ConfigDir(), - }, - }, - ProxyAddr: envoyInstance.LocalAddr() + ":8080", - BindAddr: fmt.Sprintf(":%v", sqoopPort), - RoleName: "sqoop-test", - VirtualServiceName: "sqoop-test", - } - eventLoop, err := Setup(opts) - Expect(err).NotTo(HaveOccurred()) - stop := make(chan struct{}) - go eventLoop.Run(stop) - - err = envoyInstance.RunWithId(opts.RoleName + "~e2e-test") - Expect(err).NotTo(HaveOccurred()) - - err = glooInstance.Run() - Expect(err).NotTo(HaveOccurred()) - - gloo, err := configstorage.Bootstrap(opts.Options) - Expect(err).NotTo(HaveOccurred()) - - _, err = gloo.V1().Upstreams().Create(starWarsUpstream()) - Expect(err).NotTo(HaveOccurred()) - - sqoop, err := bootstrap.Bootstrap(opts.Options) - Expect(err).NotTo(HaveOccurred()) - - _, err = sqoop.V1().Schemas().Create(test.StarWarsV1Schema()) - Expect(err).NotTo(HaveOccurred()) - - _, err = sqoop.V1().ResolverMaps().Create(test.StarWarsResolverMap()) - Expect(err).NotTo(HaveOccurred()) - - // it should create the virtual service - var virtualServices []*gloov1.VirtualService - Eventually(func() ([]*gloov1.VirtualService, error) { - virtualServices, err = gloo.V1().VirtualServices().List() - return virtualServices, err - }, time.Second*2).Should(HaveLen(1)) - Expect(virtualServices[0].Name).To(Equal(opts.VirtualServiceName)) - - // it should create the role - var roles []*gloov1.Role - Eventually(func() ([]*gloov1.Role, error) { - roles, err = gloo.V1().Roles().List() - return roles, err - }, time.Second*2).Should(HaveLen(1)) - Expect(roles[0].Name).To(Equal(opts.VirtualServiceName)) - Expect(roles[0].Listeners).To(HaveLen(1)) - Expect(roles[0].Listeners[0].BindPort).To(Equal(uint32(8080))) - Expect(roles[0].Listeners[0].VirtualServices).To(HaveLen(1)) - Expect(roles[0].Listeners[0].VirtualServices[0]).To(Equal(opts.VirtualServiceName)) - - eventuallyQueryShouldRespond(`{"query": "{hero{name}}"}`, - `{"data":{"hero":{"name":"R2-D2"}}}`) - - eventuallyQueryShouldRespond(`{"query": "{human(id: 1001){name friends{name}}}"}`, - `{"data":{"human":{"name":"Darth Vader","friends":[{"name":"Wilhuff Tarkin"}]}}}`) - - }) -}) - -func eventuallyQueryShouldRespond(queryString, expectedString string) { - Eventually(func() (string, error) { - res, err := http.Post(fmt.Sprintf("http://localhost:%v/starwars-schema/query", sqoopPort), - "", - bytes.NewBuffer([]byte(queryString))) - if err != nil { - return "", err - } - if res.StatusCode != 200 { - return "", errors.Errorf("bad status code %v", res.StatusCode) - } - b, err := ioutil.ReadAll(res.Body) - if err != nil { - return "", err - } - return string(b), nil - }, time.Second*45).Should(ContainSubstring(expectedString)) -} - -func ptr(str string) *types.StringValue { - return &types.StringValue{Value: str} -} - -func starWarsUpstream() *gloov1.Upstream { - return &gloov1.Upstream{ - Name: "starwars-rest", - Type: static.UpstreamTypeService, - Spec: static.EncodeUpstreamSpec(&static.UpstreamSpec{ - Hosts: []*static.Host{ - { - Addr: "localhost", - Port: starWarsPort, - }, - }, - }), - ServiceInfo: &gloov1.ServiceInfo{ - Type: rest.ServiceTypeREST, - }, - Functions: []*gloov1.Function{ - { - Name: "GetHero", - Spec: rest.EncodeFunctionSpec(rest.TransformationSpec{ - Headers: map[string]string{":method": "GET"}, - Path: "/api/hero", - }), - }, - { - Name: "GetCharacter", - Spec: rest.EncodeFunctionSpec(rest.TransformationSpec{ - Body: ptr(""), - Headers: map[string]string{ - "x-id": "{{id}}", - ":method": "GET", - }, - Path: "/api/character", - }), - }, - { - Name: "GetCharacters", - Spec: rest.EncodeFunctionSpec(rest.TransformationSpec{ - Headers: map[string]string{ - ":method": "POST", - }, - Path: "/api/characters", - }), - }, - }, - } -} diff --git a/pkg/core/event_loop.go b/pkg/core/event_loop.go deleted file mode 100644 index 2055dee..0000000 --- a/pkg/core/event_loop.go +++ /dev/null @@ -1,239 +0,0 @@ -package core - -import ( - "fmt" - "net/http" - - "github.com/hashicorp/go-multierror" - "github.com/pkg/errors" - gloobootstrap "github.com/solo-io/gloo/pkg/bootstrap" - "github.com/solo-io/gloo/pkg/bootstrap/configstorage" - "github.com/solo-io/gloo/pkg/log" - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/bootstrap" - "github.com/solo-io/sqoop/pkg/configwatcher" - "github.com/solo-io/sqoop/pkg/exec" - "github.com/solo-io/sqoop/pkg/graphql" - "github.com/solo-io/sqoop/pkg/operator" - "github.com/solo-io/sqoop/pkg/reporter" - "github.com/solo-io/sqoop/pkg/resolvers" - "github.com/solo-io/sqoop/pkg/storage" - "github.com/solo-io/sqoop/pkg/util" - "github.com/vektah/gqlgen/neelance/schema" -) - -type EventLoop struct { - cfgWatcher configwatcher.Interface - operator *operator.GlooOperator - router *graphql.Router - sqoop storage.Interface - reporter reporter.Interface - proxyAddr string - bindAddr string -} - -func Setup(opts bootstrap.Options) (*EventLoop, error) { - gloo, err := configstorage.Bootstrap(opts.Options) - if err != nil { - return nil, errors.Wrap(err, "creating gloo client") - } - sqoop, err := bootstrap.Bootstrap(opts.Options) - if err != nil { - return nil, errors.Wrap(err, "creating sqoop client") - } - switch opts.ConfigStorageOptions.Type { - case gloobootstrap.WatcherTypeFile: - log.Printf("Sqoop storage options: %v", opts.FileOptions) - case gloobootstrap.WatcherTypeConsul: - log.Printf("Sqoop storage options: %v", opts.ConsulOptions) - case gloobootstrap.WatcherTypeKube: - log.Printf("Sqoop storage options: %v", opts.KubeOptions) - } - if err := gloo.V1().Register(); err != nil { - return nil, errors.Wrap(err, "registering gloo client") - } - if err := sqoop.V1().Register(); err != nil { - return nil, errors.Wrap(err, "registering sqoop storage client") - } - cfgWatcher, err := configwatcher.NewConfigWatcher(sqoop) - if err != nil { - return nil, errors.Wrap(err, "starting watch for Sqoop config") - } - op := operator.NewGlooOperator(gloo, opts.VirtualServiceName, opts.RoleName) - router := graphql.NewRouter() - rep := reporter.NewReporter(sqoop) - return &EventLoop{ - cfgWatcher: cfgWatcher, - operator: op, - router: router, - sqoop: sqoop, - reporter: rep, - proxyAddr: opts.ProxyAddr, - bindAddr: opts.BindAddr, - }, nil -} - -func sendErr(errs chan error, err error) { - go func(err error) { - errs <- errors.Wrap(err, "update failed") - }(err) -} - -func (el *EventLoop) Run(stop <-chan struct{}) { - go el.cfgWatcher.Run(stop) - go func() { - log.Printf("Sqoop server started and listening on %v", el.bindAddr) - log.Fatalf("failed to start server: %v", http.ListenAndServe(el.bindAddr, el.router)) - }() - errs := make(chan error) - for { - select { - case cfg := <-el.cfgWatcher.Config(): - if err := el.update(cfg); err != nil { - sendErr(errs, errors.Wrap(err, "update failed")) - } - case err := <-el.cfgWatcher.Error(): - sendErr(errs, errors.Wrap(err, "config watcher error")) - case err := <-errs: - log.Warnf("error in event loop: %v", err) - case <-stop: - return - } - } -} - -func configErrs(reports []reporter.ConfigObjectReport) error { - var errs error - for _, report := range reports { - if report.Err != nil { - errs = multierror.Append(errs, report.Err) - } - } - return errs -} - -func (el *EventLoop) update(cfg *v1.Config) error { - endpoints, reports := el.createGraphqlEndpoints(cfg) - el.router.UpdateEndpoints(endpoints...) - errs := configErrs(reports) - if err := el.reporter.WriteReports(reports); err != nil { - errs = multierror.Append(errs, err) - } - if err := el.operator.ConfigureGloo(); err != nil { - errs = multierror.Append(errs, err) - } - return errs -} - -func (el *EventLoop) createGraphqlEndpoints(cfg *v1.Config) ([]*graphql.Endpoint, []reporter.ConfigObjectReport) { - var ( - endpoints []*graphql.Endpoint - schemaReports []reporter.ConfigObjectReport - resolverMapReports []reporter.ConfigObjectReport - ) - resolverMapErrs := make(map[*v1.ResolverMap]error) - - for _, schema := range cfg.Schemas { - schemaReport := reporter.ConfigObjectReport{ - CfgObject: schema, - } - // empty map means we should generate a skeleton and update the schema to point to it - ep, schemaErr, resolverMapErr := el.handleSchema(schema, cfg.ResolverMaps) - if schemaErr != nil { - resolverMapErr.err = multierror.Append(resolverMapErr.err, errors.Wrap(schemaErr, "schema was not accepted")) - } - if resolverMapErr.resolverMap != nil { - err := resolverMapErrs[resolverMapErr.resolverMap] - if resolverMapErr.err != nil { - err = multierror.Append(resolverMapErrs[resolverMapErr.resolverMap], resolverMapErr.err) - } - resolverMapErrs[resolverMapErr.resolverMap] = err - } - schemaReport.Err = schemaErr - schemaReports = append(schemaReports, schemaReport) - if ep == nil { - continue - } - endpoints = append(endpoints, ep) - } - for resolverMap, err := range resolverMapErrs { - resolverMapReports = append(resolverMapReports, reporter.ConfigObjectReport{ - CfgObject: resolverMap, - Err: err, - }) - } - return endpoints, append(schemaReports, resolverMapReports...) -} - -type resolverMapError struct { - resolverMap *v1.ResolverMap - err error -} - -func (el *EventLoop) handleSchema(schema *v1.Schema, resolvers []*v1.ResolverMap) (*graphql.Endpoint, error, resolverMapError) { - if schema.ResolverMap == "" { - return nil, el.createEmptyResolverMap(schema), resolverMapError{} - } - for _, resolverMap := range resolvers { - if resolverMap.Name == schema.ResolverMap { - ep, schemaErr, resolverErr := el.createGraphqlEndpoint(schema, resolverMap) - return ep, schemaErr, resolverMapError{resolverMap: resolverMap, err: resolverErr} - } - } - return nil, errors.Errorf("resolver map %v for schema %v not found", schema.ResolverMap, schema.Name), resolverMapError{} -} - -// create an empty resolver map and -func (el *EventLoop) createEmptyResolverMap(schema *v1.Schema) error { - resolverName := resolverMapName(schema) - parsedSchema, err := parseSchemaString(schema) - if err != nil { - return errors.Wrap(err, "failed to parse schema") - } - generatedResolvers := util.GenerateResolverMapSkeleton(resolverName, parsedSchema) - - // update existing schema with the new schema name - // important to do this first or we may retry creating the resolver map in a race - schemaToUpdate, err := el.sqoop.V1().Schemas().Get(schema.Name) - if err != nil { - return errors.Wrapf(err, "retrieving schema %v from storage", schema.Name) - } - schemaToUpdate.ResolverMap = resolverName - if _, err := el.sqoop.V1().Schemas().Update(schemaToUpdate); err != nil { - return errors.Wrapf(err, "updating schema %v in storage", schema.Name) - } - - if _, err := el.sqoop.V1().ResolverMaps().Create(generatedResolvers); err != nil { - return errors.Wrapf(err, "writing resolver map %v to storage", resolverName) - } - return nil -} - -func (el *EventLoop) createGraphqlEndpoint(schema *v1.Schema, resolverMap *v1.ResolverMap) (*graphql.Endpoint, error, error) { - resolverFactory := resolvers.NewResolverFactory(el.proxyAddr, resolverMap) - parsedSchema, err := parseSchemaString(schema) - if err != nil { - return nil, errors.Wrap(err, "failed to parse schema"), nil - } - executableResolvers, err := exec.NewExecutableResolvers(parsedSchema, resolverFactory.CreateResolver) - if err != nil { - return nil, nil, errors.Wrap(err, "failed to generate resolvers from map") - } - el.operator.ApplyResolvers(resolverMap) - executableSchema := exec.NewExecutableSchema(parsedSchema, executableResolvers) - return &graphql.Endpoint{ - SchemaName: schema.Name, - RootPath: "/" + schema.Name, - QueryPath: "/" + schema.Name + "/query", - ExecSchema: executableSchema, - }, nil, nil -} - -func parseSchemaString(sch *v1.Schema) (*schema.Schema, error) { - parsedSchema := schema.New() - return parsedSchema, parsedSchema.Parse(sch.InlineSchema) -} - -func resolverMapName(schema *v1.Schema) string { - return fmt.Sprintf("%v-resolvers", schema.Name) -} diff --git a/pkg/defaults/main.go b/pkg/defaults/main.go new file mode 100644 index 0000000..d81409e --- /dev/null +++ b/pkg/defaults/main.go @@ -0,0 +1,5 @@ +package defaults + +const ( + GlooSystem = "gloo-system" +) diff --git a/pkg/dynamic/values.go b/pkg/engine/dynamic/values.go similarity index 100% rename from pkg/dynamic/values.go rename to pkg/engine/dynamic/values.go diff --git a/pkg/exec/executable_resolvers.go b/pkg/engine/exec/executable_resolvers.go similarity index 97% rename from pkg/exec/executable_resolvers.go rename to pkg/engine/exec/executable_resolvers.go index ff16c2c..0fd30dc 100644 --- a/pkg/exec/executable_resolvers.go +++ b/pkg/engine/exec/executable_resolvers.go @@ -6,7 +6,7 @@ import ( "time" "github.com/pkg/errors" - "github.com/solo-io/sqoop/pkg/dynamic" + "github.com/solo-io/sqoop/pkg/engine/dynamic" "github.com/vektah/gqlgen/neelance/common" "github.com/vektah/gqlgen/neelance/schema" ) @@ -76,13 +76,13 @@ func toValue(data []byte, typ common.Type) (dynamic.Value, error) { case *schema.Object, *schema.Interface: var rawResult map[string]interface{} if err := json.Unmarshal(data, &rawResult); err != nil { - return nil, errors.Wrap(err, "parsing response as json") + return nil, errors.Wrapf(err, "parsing response '%s' as json object", data) } return convertValue(fieldType, rawResult) case *common.List: var rawResult []interface{} if err := json.Unmarshal(data, &rawResult); err != nil { - return nil, errors.Wrap(err, "parsing response as json") + return nil, errors.Wrapf(err, "parsing response '%s='as json array", data) } return convertValue(fieldType, rawResult) case *schema.Scalar: diff --git a/pkg/exec/executable_schema.go b/pkg/engine/exec/executable_schema.go similarity index 99% rename from pkg/exec/executable_schema.go rename to pkg/engine/exec/executable_schema.go index c5bf9c7..4a4fd7a 100644 --- a/pkg/exec/executable_schema.go +++ b/pkg/engine/exec/executable_schema.go @@ -7,7 +7,7 @@ import ( "strconv" "github.com/pkg/errors" - "github.com/solo-io/sqoop/pkg/dynamic" + "github.com/solo-io/sqoop/pkg/engine/dynamic" "github.com/vektah/gqlgen/graphql" "github.com/vektah/gqlgen/neelance/introspection" "github.com/vektah/gqlgen/neelance/query" diff --git a/pkg/engine/graphql_engine.go b/pkg/engine/graphql_engine.go new file mode 100644 index 0000000..ed84ecd --- /dev/null +++ b/pkg/engine/graphql_engine.go @@ -0,0 +1,53 @@ +package engine + +import ( + "github.com/pkg/errors" + "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" + v1 "github.com/solo-io/sqoop/pkg/api/v1" + "github.com/solo-io/sqoop/pkg/engine/exec" + "github.com/solo-io/sqoop/pkg/engine/resolvers" + "github.com/solo-io/sqoop/pkg/engine/router" + "github.com/vektah/gqlgen/neelance/schema" +) + +type Engine struct { + sidecarAddr string +} + +func NewEngine(sidecarAddr string) *Engine { + return &Engine{sidecarAddr: sidecarAddr} +} + +// first error is for schema +// second is for resolvermap +func (en *Engine) CreateGraphqlEndpoint(schema *v1.Schema, resolverMap *v1.ResolverMap) (*router.Endpoint, error, error) { + resolverFactory := resolvers.NewResolverFactory(en.sidecarAddr, resolverMap) + parsedSchema, err := parseSchemaString(schema) + if err != nil { + return nil, errors.Wrap(err, "failed to parse schema"), nil + } + executableResolvers, err := exec.NewExecutableResolvers(parsedSchema, resolverFactory.CreateResolver) + if err != nil { + return nil, nil, errors.Wrap(err, "failed to generate executable resolvers from map") + } + executableSchema := exec.NewExecutableSchema(parsedSchema, executableResolvers) + return &router.Endpoint{ + SchemaName: schema.Metadata.Name, + RootPath: SqoopPlaygroundPath(schema.Metadata.Ref()), + QueryPath: SqoopQueryPath(schema.Metadata.Ref()), + ExecSchema: executableSchema, + }, nil, nil +} + +func parseSchemaString(sch *v1.Schema) (*schema.Schema, error) { + parsedSchema := schema.New() + return parsedSchema, parsedSchema.Parse(sch.InlineSchema) +} + +func SqoopQueryPath(schemaRef core.ResourceRef) string { + return "/" + schemaRef.Namespace + "/" + schemaRef.Name + "/query" +} + +func SqoopPlaygroundPath(schemaRef core.ResourceRef) string { + return "/" + schemaRef.Namespace + "/" + schemaRef.Name +} diff --git a/pkg/resolvers/factory.go b/pkg/engine/resolvers/factory.go similarity index 59% rename from pkg/resolvers/factory.go rename to pkg/engine/resolvers/factory.go index 8f6e753..9cd59e9 100644 --- a/pkg/resolvers/factory.go +++ b/pkg/engine/resolvers/factory.go @@ -2,11 +2,11 @@ package resolvers import ( "github.com/pkg/errors" - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/exec" - "github.com/solo-io/sqoop/pkg/resolvers/gloo" - "github.com/solo-io/sqoop/pkg/resolvers/node" - "github.com/solo-io/sqoop/pkg/resolvers/template" + v1 "github.com/solo-io/sqoop/pkg/api/v1" + "github.com/solo-io/sqoop/pkg/engine/exec" + "github.com/solo-io/sqoop/pkg/engine/resolvers/gloo" + "github.com/solo-io/sqoop/pkg/engine/resolvers/node" + "github.com/solo-io/sqoop/pkg/engine/resolvers/template" ) type ResolverFactory struct { @@ -23,27 +23,27 @@ func NewResolverFactory(proxyAddr string, resolverMap *v1.ResolverMap) *Resolver func (rf *ResolverFactory) CreateResolver(typeName, fieldName string) (exec.RawResolver, error) { if len(rf.resolverMap.Types) == 0 { - return nil, errors.Errorf("no types defined in resolver map %v", rf.resolverMap.Name) + return nil, errors.Errorf("no types defined in resolver map %v", rf.resolverMap.Metadata.Ref()) } typeResolver, ok := rf.resolverMap.Types[typeName] if !ok { - return nil, errors.Errorf("type %v not found in resolver map %v", typeName, rf.resolverMap.Name) + return nil, errors.Errorf("type %v not found in resolver map %v", typeName, rf.resolverMap.Metadata.Ref()) } if len(typeResolver.Fields) == 0 { - return nil, errors.Errorf("no fields defined for type %v in resolver map %v", typeName, rf.resolverMap.Name) + return nil, errors.Errorf("no fields defined for type %v in resolver map %v", typeName, rf.resolverMap.Metadata.Ref()) } fieldResolver, ok := typeResolver.Fields[fieldName] if !ok { return nil, errors.Errorf("field %v not found for type %v in resolver map %v", - fieldName, typeResolver, rf.resolverMap.Name) + fieldName, typeResolver, rf.resolverMap.Metadata.Ref()) } switch resolver := fieldResolver.Resolver.(type) { - case *v1.Resolver_NodejsResolver: + case *v1.FieldResolver_NodejsResolver: return node.NewNodeResolver(resolver.NodejsResolver) - case *v1.Resolver_TemplateResolver: - return template.NewTemplateResolver(resolver.TemplateResolver) - case *v1.Resolver_GlooResolver: - return rf.glooResolverFactory.CreateResolver(typeName, fieldName, resolver.GlooResolver) + case *v1.FieldResolver_TemplateResolver: + return template.NewTemplateResolver(resolver.TemplateResolver.InlineTemplate) + case *v1.FieldResolver_GlooResolver: + return rf.glooResolverFactory.CreateResolver(rf.resolverMap.Metadata.Ref(), typeName, fieldName, resolver.GlooResolver) } // no resolver has been defined return nil, nil diff --git a/pkg/resolvers/gloo/gloo_resolvers.go b/pkg/engine/resolvers/gloo/gloo_resolvers.go similarity index 57% rename from pkg/resolvers/gloo/gloo_resolvers.go rename to pkg/engine/resolvers/gloo/gloo_resolvers.go index f49b8a2..4e78316 100644 --- a/pkg/resolvers/gloo/gloo_resolvers.go +++ b/pkg/engine/resolvers/gloo/gloo_resolvers.go @@ -8,10 +8,11 @@ import ( "text/template" "github.com/pkg/errors" - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/exec" - "github.com/solo-io/sqoop/pkg/operator" - "github.com/solo-io/sqoop/pkg/util" + "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" + v1 "github.com/solo-io/sqoop/pkg/api/v1" + "github.com/solo-io/sqoop/pkg/engine/exec" + "github.com/solo-io/sqoop/pkg/engine/util" + "github.com/solo-io/sqoop/pkg/translator" ) type ResolverFactory struct { @@ -24,41 +25,52 @@ func NewResolverFactory(proxyAddr string) *ResolverFactory { } } -func (rf *ResolverFactory) CreateResolver(typeName, fieldName string, glooResolver *v1.GlooResolver) (exec.RawResolver, error) { +// TODO(ilackarms): support more than just Body in request/response template +func (rf *ResolverFactory) CreateResolver(resolverMap core.ResourceRef, typeName, fieldName string, glooResolver *v1.GlooResolver) (exec.RawResolver, error) { requestBodyTemplate := glooResolver.RequestTemplate responseBodyTemplate := glooResolver.ResponseTemplate - contentType := glooResolver.ContentType - if contentType == "" { - contentType = "application/json" + + contentType := "application/json" + if requestBodyTemplate != nil { + // TODO(ilackarms): find package that allows us to convert from http1->http2 header style + ct := requestBodyTemplate.Headers[":content-type"] + if ct != "" { + contentType = ct + } + ct = requestBodyTemplate.Headers["Content-Type"] + if ct != "" { + contentType = ct + } } + var ( requestTemplate *template.Template responseTemplate *template.Template err error ) - if requestBodyTemplate != "" { - requestTemplate, err = util.Template(requestBodyTemplate) + if requestBodyTemplate != nil { + requestTemplate, err = util.Template(requestBodyTemplate.Body) if err != nil { return nil, errors.Wrap(err, "parsing request body template failed") } } - if responseBodyTemplate != "" { - responseTemplate, err = util.Template(responseBodyTemplate) + if responseBodyTemplate != nil { + responseTemplate, err = util.Template(responseBodyTemplate.Body) if err != nil { return nil, errors.Wrap(err, "parsing response body template failed") } } - return rf.newResolver(typeName, fieldName, contentType, requestTemplate, responseTemplate), nil + return rf.newResolver(resolverMap, typeName, fieldName, contentType, requestTemplate, responseTemplate), nil } -func (rf *ResolverFactory) newResolver(typeName, fieldName string, contentType string, requestTemplate, responseTemplate *template.Template) exec.RawResolver { +func (rf *ResolverFactory) newResolver(resolverMap core.ResourceRef, typeName, fieldName string, contentType string, requestTemplate, responseTemplate *template.Template) exec.RawResolver { return func(params exec.Params) ([]byte, error) { body := &bytes.Buffer{} switch { - case requestTemplate != nil : + case requestTemplate != nil: buf, err := util.ExecTemplate(requestTemplate, params) if err != nil { // TODO: sanitize @@ -71,7 +83,7 @@ func (rf *ResolverFactory) newResolver(typeName, fieldName string, contentType s } } - url := "http://" + rf.proxyAddr + operator.RoutePath(typeName, fieldName) + url := "http://" + rf.proxyAddr + translator.RoutePath(resolverMap, typeName, fieldName) res, err := http.Post(url, contentType, body) if err != nil { return nil, errors.Wrap(err, "performing http post") @@ -96,20 +108,14 @@ func (rf *ResolverFactory) newResolver(typeName, fieldName string, contentType s return data, nil } - // requires output to be json object - var result map[string]interface{} + var result interface{} if err := json.Unmarshal(data, &result); err != nil { return nil, errors.Wrap(err, "failed to parse response as json object. "+ "response templates may only be used with JSON responses") } - input := struct { - Result map[string]interface{} - }{ - Result: result, - } buf := &bytes.Buffer{} - if err := responseTemplate.Execute(buf, input); err != nil { - return nil, errors.Wrapf(err, "executing response template for response %v", input) + if err := responseTemplate.Execute(buf, result); err != nil { + return nil, errors.Wrapf(err, "executing response template for response %v", result) } return buf.Bytes(), nil } diff --git a/pkg/resolvers/node/node_resolvers.go b/pkg/engine/resolvers/node/node_resolvers.go similarity index 69% rename from pkg/resolvers/node/node_resolvers.go rename to pkg/engine/resolvers/node/node_resolvers.go index 9e2966a..ec304f4 100644 --- a/pkg/resolvers/node/node_resolvers.go +++ b/pkg/engine/resolvers/node/node_resolvers.go @@ -2,8 +2,8 @@ package node import ( "github.com/pkg/errors" - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/exec" + v1 "github.com/solo-io/sqoop/pkg/api/v1" + "github.com/solo-io/sqoop/pkg/engine/exec" ) func NewNodeResolver(resolver *v1.NodeJSResolver) (exec.RawResolver, error) { diff --git a/pkg/resolvers/template/template_resolvers.go b/pkg/engine/resolvers/template/template_resolvers.go similarity index 57% rename from pkg/resolvers/template/template_resolvers.go rename to pkg/engine/resolvers/template/template_resolvers.go index 18e809f..ba283b8 100644 --- a/pkg/resolvers/template/template_resolvers.go +++ b/pkg/engine/resolvers/template/template_resolvers.go @@ -2,13 +2,12 @@ package template import ( "github.com/pkg/errors" - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/exec" - "github.com/solo-io/sqoop/pkg/util" + "github.com/solo-io/sqoop/pkg/engine/exec" + "github.com/solo-io/sqoop/pkg/engine/util" ) -func NewTemplateResolver(resolver *v1.TemplateResolver) (exec.RawResolver, error) { - tmpl, err := util.Template(resolver.InlineTemplate) +func NewTemplateResolver(inlineTemplate string) (exec.RawResolver, error) { + tmpl, err := util.Template(inlineTemplate) if err != nil { return nil, errors.Wrap(err, "parsing inline template") } diff --git a/pkg/engine/router/landing_page.go b/pkg/engine/router/landing_page.go new file mode 100644 index 0000000..e1e7ed5 --- /dev/null +++ b/pkg/engine/router/landing_page.go @@ -0,0 +1,52 @@ +package router + +const landingPageTemplateString = ` + + + + + + + Landing Page + + + + + + + + + +` diff --git a/pkg/graphql/router.go b/pkg/engine/router/router.go similarity index 94% rename from pkg/graphql/router.go rename to pkg/engine/router/router.go index 56c3823..f09a16b 100644 --- a/pkg/graphql/router.go +++ b/pkg/engine/router/router.go @@ -1,17 +1,18 @@ -package graphql +package router import ( "context" "net/http" "sync" + "bytes" + "fmt" + "html/template" + "github.com/gorilla/mux" - "github.com/solo-io/gloo/pkg/log" + "github.com/solo-io/solo-kit/pkg/utils/log" "github.com/vektah/gqlgen/graphql" "github.com/vektah/gqlgen/handler" - "fmt" - "html/template" - "bytes" ) type Router struct { @@ -37,7 +38,7 @@ type Endpoint struct { ExecSchema graphql.ExecutableSchema } -func (s *Router) UpdateEndpoints(endpoints ...*Endpoint) { +func (s *Router) UpdateEndpoints(endpoints []*Endpoint) { m := mux.NewRouter() for _, endpoint := range endpoints { m.Handle(endpoint.RootPath, handler.Playground(endpoint.SchemaName, endpoint.QueryPath)) diff --git a/pkg/util/template_utils.go b/pkg/engine/util/template_utils.go similarity index 95% rename from pkg/util/template_utils.go rename to pkg/engine/util/template_utils.go index 89ed303..cd51369 100644 --- a/pkg/util/template_utils.go +++ b/pkg/engine/util/template_utils.go @@ -5,7 +5,7 @@ import ( "encoding/json" "text/template" - "github.com/solo-io/sqoop/pkg/exec" + "github.com/solo-io/sqoop/pkg/engine/exec" ) func Template(tmplString string) (*template.Template, error) { diff --git a/pkg/graphql/graphql_suite_test.go b/pkg/graphql/graphql_suite_test.go deleted file mode 100644 index 5130e2b..0000000 --- a/pkg/graphql/graphql_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package graphql_test - -import ( - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -func TestGraphql(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Graphql Suite") -} diff --git a/pkg/graphql/landing_page.go b/pkg/graphql/landing_page.go deleted file mode 100644 index 9b44eca..0000000 --- a/pkg/graphql/landing_page.go +++ /dev/null @@ -1,147 +0,0 @@ -package graphql - -const landingPageTemplateString = ` - - - - - - - Landing Page - - - - - - - - -
    - -
    - - - - - -` - diff --git a/pkg/operator/gloo_operator.go b/pkg/operator/gloo_operator.go deleted file mode 100644 index ec6189c..0000000 --- a/pkg/operator/gloo_operator.go +++ /dev/null @@ -1,192 +0,0 @@ -package operator - -import ( - "github.com/pkg/errors" - "github.com/solo-io/gloo/pkg/api/types/v1" - "github.com/solo-io/gloo/pkg/storage" - sqoopv1 "github.com/solo-io/sqoop/pkg/api/types/v1" -) - -const listenerPort = uint32(8080) - -type GlooOperator struct { - gloo storage.Interface - virtualServiceName string - roleName string - cachedRoutes []route -} - -func NewGlooOperator(gloo storage.Interface, virtualServiceName string, roleName string) *GlooOperator { - return &GlooOperator{ - gloo: gloo, - virtualServiceName: virtualServiceName, - roleName: roleName, - } -} - -type route struct { - path string - destinations []destination -} - -type destination struct { - upstreamName, functionName string - weight uint32 -} - -// apply routes to Gloo and clear cache -func (operator *GlooOperator) ConfigureGloo() error { - // VirtualService - desiredVirtualService, err := operator.desiredVirtualService(operator.cachedRoutes) - if err != nil { - return errors.Wrap(err, "invalid resolver routes") - } - existingVirtualService, err := operator.gloo.V1().VirtualServices().Get(operator.virtualServiceName) - if err != nil { - if _, err := operator.gloo.V1().VirtualServices().Create(desiredVirtualService); err != nil { - return err - } - } else { - if !routesEqual(existingVirtualService.Routes, desiredVirtualService.Routes) { - desiredVirtualService.Metadata.ResourceVersion = existingVirtualService.Metadata.ResourceVersion - if _, err = operator.gloo.V1().VirtualServices().Update(desiredVirtualService); err != nil { - return err - } - } - } - - // Role - desiredRole := operator.desiredRole() - existingRole, err := operator.gloo.V1().Roles().Get(operator.roleName) - if err != nil { - if _, err := operator.gloo.V1().Roles().Create(desiredRole); err != nil { - return err - } - } else { - if !listenersEqual(existingRole.Listeners, desiredRole.Listeners) { - desiredRole.Metadata.ResourceVersion = existingRole.Metadata.ResourceVersion - if _, err = operator.gloo.V1().Roles().Update(desiredRole); err != nil { - return err - } - } - } - - // clear cache - operator.cachedRoutes = nil - - return nil -} - -func (operator *GlooOperator) ApplyResolvers(resolverMap *sqoopv1.ResolverMap) { - operator.cachedRoutes = append(operator.cachedRoutes, buildRoutes(resolverMap)...) -} - -func routesEqual(list1, list2 []*v1.Route) bool { - if len(list1) != len(list2) { - return false - } - for i := range list1 { - r1, r2 := list1[i], list2[i] - if !r1.Equal(r2) { - return false - } - } - return true -} - -func listenersEqual(list1, list2 []*v1.Listener) bool { - if len(list1) != len(list2) { - return false - } - for i := range list1 { - r1, r2 := list1[i], list2[i] - if !r1.Equal(r2) { - return false - } - } - return true -} - -func (operator *GlooOperator) desiredVirtualService(resolverRoutes []route) (*v1.VirtualService, error) { - var routes []*v1.Route - for _, rr := range resolverRoutes { - route, err := resolverRoute(rr) - if err != nil { - return nil, errors.Wrap(err, "creating route for resolver") - } - routes = append(routes, route) - } - return &v1.VirtualService{ - Name: operator.virtualServiceName, - Domains: []string{"*"}, - Routes: routes, - Metadata: &v1.Metadata{}, - DisableForGateways: true, - }, nil -} - -func (operator *GlooOperator) desiredRole() *v1.Role { - return &v1.Role{ - Name: operator.roleName, - Listeners: []*v1.Listener{ - { - Name: "graphql-port", - BindAddress: "::", - BindPort: listenerPort, - VirtualServices: []string{operator.virtualServiceName}, - }, - }, - Metadata: &v1.Metadata{}, - } -} - -func resolverRoute(route route) (*v1.Route, error) { - if len(route.destinations) == 0 { - return nil, errors.Errorf("need at least 1 destination to create a resolver route") - } - - var ( - singleDestination *v1.Destination - multiDestination []*v1.WeightedDestination - ) - destinations := route.destinations - - switch { - case len(destinations) == 1: - singleDestination = &v1.Destination{ - DestinationType: &v1.Destination_Function{ - Function: &v1.FunctionDestination{ - UpstreamName: destinations[0].upstreamName, - FunctionName: destinations[0].functionName, - }, - }, - } - case len(destinations) > 1: - for _, dest := range destinations { - multiDestination = append(multiDestination, &v1.WeightedDestination{ - Destination: &v1.Destination{ - DestinationType: &v1.Destination_Function{ - Function: &v1.FunctionDestination{ - UpstreamName: dest.upstreamName, - FunctionName: dest.functionName, - }, - }, - }, - Weight: dest.weight, - }) - } - } - - return &v1.Route{ - Matcher: &v1.Route_RequestMatcher{ - RequestMatcher: &v1.RequestMatcher{ - Path: &v1.RequestMatcher_PathExact{ - PathExact: route.path, - }, - Verbs: []string{"POST"}, - }, - }, - MultipleDestinations: multiDestination, - SingleDestination: singleDestination, - }, nil -} diff --git a/pkg/operator/gloo_operator_test.go b/pkg/operator/gloo_operator_test.go deleted file mode 100644 index 70cb87b..0000000 --- a/pkg/operator/gloo_operator_test.go +++ /dev/null @@ -1,160 +0,0 @@ -package operator_test - -import ( - "io/ioutil" - "os" - "time" - - "github.com/gogo/protobuf/types" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "github.com/solo-io/gloo/pkg/api/types/v1" - "github.com/solo-io/gloo/pkg/storage" - "github.com/solo-io/gloo/pkg/storage/file" - . "github.com/solo-io/sqoop/pkg/operator" - "github.com/solo-io/sqoop/test" -) - -var _ = Describe("GlooOperator", func() { - var ( - tmpDir string - gloo storage.Interface - vServiceName, roleName = "test-virtualservice", "test-role" - operator *GlooOperator - ) - BeforeEach(func() { - var err error - tmpDir, err = ioutil.TempDir("", "") - Expect(err).NotTo(HaveOccurred()) - gloo, err = file.NewStorage(tmpDir, time.Millisecond) - Expect(err).NotTo(HaveOccurred()) - err = gloo.V1().Register() - Expect(err).NotTo(HaveOccurred()) - operator = NewGlooOperator(gloo, vServiceName, roleName) - }) - AfterEach(func() { - os.RemoveAll(tmpDir) - }) - It("creates the virtualservice with all the required routes", func() { - operator.ApplyResolvers(test.StarWarsResolverMap()) - err := operator.ConfigureGloo() - Expect(err).NotTo(HaveOccurred()) - virtualService, err := gloo.V1().VirtualServices().Get(vServiceName) - Expect(err).NotTo(HaveOccurred()) - Expect(virtualService.Routes).To(HaveLen(5)) - Expect(virtualService.Routes[0]).To(Equal(&v1.Route{ - Matcher: &v1.Route_RequestMatcher{ - RequestMatcher: &v1.RequestMatcher{ - Path: &v1.RequestMatcher_PathExact{ - PathExact: "/Droid.friends", - }, - Verbs: []string{ - "POST", - }, - }, - }, - SingleDestination: &v1.Destination{ - DestinationType: &v1.Destination_Function{ - Function: &v1.FunctionDestination{ - UpstreamName: "starwars-rest", - FunctionName: "GetCharacters", - }, - }, - }, - })) - Expect(virtualService.Routes[1]).To(Equal(&v1.Route{ - Matcher: &v1.Route_RequestMatcher{ - RequestMatcher: &v1.RequestMatcher{ - Path: &v1.RequestMatcher_PathExact{ - PathExact: "/Human.friends", - }, - Verbs: []string{ - "POST", - }, - }, - }, - SingleDestination: &v1.Destination{ - DestinationType: &v1.Destination_Function{ - Function: &v1.FunctionDestination{ - UpstreamName: "starwars-rest", - FunctionName: "GetCharacters", - }, - }, - }, - })) - Expect(virtualService.Routes[2]).To(Equal(&v1.Route{ - Matcher: &v1.Route_RequestMatcher{ - RequestMatcher: &v1.RequestMatcher{ - Path: &v1.RequestMatcher_PathExact{ - PathExact: "/Query.droid", - }, - Verbs: []string{ - "POST", - }, - }, - }, - SingleDestination: &v1.Destination{ - DestinationType: &v1.Destination_Function{ - Function: &v1.FunctionDestination{ - UpstreamName: "starwars-rest", - FunctionName: "GetCharacter", - }, - }, - }, - PrefixRewrite: "", - Extensions: (*types.Struct)(nil), - })) - Expect(virtualService.Routes[3]).To(Equal(&v1.Route{ - Matcher: &v1.Route_RequestMatcher{ - RequestMatcher: &v1.RequestMatcher{ - Path: &v1.RequestMatcher_PathExact{ - PathExact: "/Query.hero", - }, - Verbs: []string{ - "POST", - }, - }, - }, - SingleDestination: &v1.Destination{ - DestinationType: &v1.Destination_Function{ - Function: &v1.FunctionDestination{ - UpstreamName: "starwars-rest", - FunctionName: "GetHero", - }, - }, - }, - PrefixRewrite: "", - Extensions: (*types.Struct)(nil), - })) - Expect(virtualService.Routes[4]).To(Equal(&v1.Route{ - Matcher: &v1.Route_RequestMatcher{ - RequestMatcher: &v1.RequestMatcher{ - Path: &v1.RequestMatcher_PathExact{ - PathExact: "/Query.human", - }, - Verbs: []string{ - "POST", - }, - }, - }, - SingleDestination: &v1.Destination{ - DestinationType: &v1.Destination_Function{ - Function: &v1.FunctionDestination{ - UpstreamName: "starwars-rest", - FunctionName: "GetCharacter", - }, - }, - }, - PrefixRewrite: "", - Extensions: (*types.Struct)(nil), - })) - Expect(virtualService.Domains).To(HaveLen(1)) - Expect(virtualService.Domains[0]).To(Equal("*")) - - role, err := gloo.V1().Roles().Get(roleName) - Expect(err).NotTo(HaveOccurred()) - Expect(role.Listeners).To(HaveLen(1)) - Expect(role.Listeners[0].VirtualServices).To(HaveLen(1)) - Expect(role.Listeners[0].VirtualServices[0]).To(Equal(vServiceName)) - }) -}) diff --git a/pkg/operator/gloo_routes.go b/pkg/operator/gloo_routes.go deleted file mode 100644 index 4fe9de5..0000000 --- a/pkg/operator/gloo_routes.go +++ /dev/null @@ -1,55 +0,0 @@ -package operator - -import ( - "fmt" - "sort" - - "github.com/solo-io/sqoop/pkg/api/types/v1" -) - -func RoutePath(typeName, fieldName string) string { - return fmt.Sprintf("/%v.%v", typeName, fieldName) -} - -func buildRoutes(resolverMap *v1.ResolverMap) []route { - var routes []route - for typeName, typeResolver := range resolverMap.Types { - for fieldName, fieldResolver := range typeResolver.Fields { - glooResolver, ok := fieldResolver.Resolver.(*v1.Resolver_GlooResolver) - if !ok { - continue - } - routes = append(routes, route{ - path: RoutePath(typeName, fieldName), - destinations: destinationsForFunction(glooResolver.GlooResolver), - }) - } - } - sort.SliceStable(routes, func(i, j int) bool { - return routes[i].path < routes[j].path - }) - return routes -} - -func destinationsForFunction(resolver *v1.GlooResolver) []destination { - switch function := resolver.Function.(type) { - case *v1.GlooResolver_SingleFunction: - return []destination{ - { - upstreamName: function.SingleFunction.Upstream, - functionName: function.SingleFunction.Function, - }, - } - case *v1.GlooResolver_MultiFunction: - var dests []destination - for _, weightedFunc := range function.MultiFunction.WeightedFunctions { - dests = append(dests, destination{ - upstreamName: weightedFunc.Function.Upstream, - functionName: weightedFunc.Function.Function, - weight: weightedFunc.Weight, - }) - } - return dests - } - panic("unknown function time") -} diff --git a/pkg/reporter/crd_reporter_test.go b/pkg/reporter/crd_reporter_test.go deleted file mode 100644 index e3c900b..0000000 --- a/pkg/reporter/crd_reporter_test.go +++ /dev/null @@ -1,154 +0,0 @@ -package reporter_test - -import ( - "os" - "path/filepath" - "time" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "github.com/pkg/errors" - "github.com/solo-io/sqoop/pkg/storage" - "github.com/solo-io/sqoop/pkg/storage/crd" - "k8s.io/client-go/tools/clientcmd" - - gloov1 "github.com/solo-io/gloo/pkg/api/types/v1" - "github.com/solo-io/gloo/pkg/log" - . "github.com/solo-io/gloo/test/helpers" - "github.com/solo-io/sqoop/pkg/api/types/v1" - . "github.com/solo-io/sqoop/pkg/reporter" - "github.com/solo-io/sqoop/test" -) - -var _ = Describe("CrdReporter", func() { - if os.Getenv("RUN_KUBE_TESTS") != "1" { - log.Printf("This test creates kubernetes resources and is disabled by default. To enable, set RUN_KUBE_TESTS=1 in your env.") - return - } - var ( - masterUrl, kubeconfigPath string - namespace string - rptr Interface - ) - BeforeEach(func() { - namespace = RandString(8) - err := SetupKubeForTest(namespace) - Must(err) - kubeconfigPath = filepath.Join(os.Getenv("HOME"), ".kube", "config") - masterUrl = "" - }) - AfterEach(func() { - TeardownKube(namespace) - }) - Describe("writereports", func() { - var ( - glooClient storage.Interface - reports []ConfigObjectReport - schemas []*v1.Schema - resolverMaps []*v1.ResolverMap - ) - Context("writes status reports for cfg crds with 0 errors", func() { - BeforeEach(func() { - reports = nil - cfg, err := clientcmd.BuildConfigFromFlags(masterUrl, kubeconfigPath) - Expect(err).NotTo(HaveOccurred()) - glooClient, err = crd.NewStorage(cfg, namespace, time.Second) - Expect(err).NotTo(HaveOccurred()) - rptr = NewReporter(glooClient) - - testCfg := newTestConfig() - schemas = testCfg.Schemas - var storables []gloov1.ConfigObject - for _, us := range schemas { - _, err := glooClient.V1().Schemas().Create(us) - Expect(err).NotTo(HaveOccurred()) - storables = append(storables, us) - } - resolverMaps = testCfg.ResolverMaps - for _, vService := range resolverMaps { - _, err := glooClient.V1().ResolverMaps().Create(vService) - Expect(err).NotTo(HaveOccurred()) - storables = append(storables, vService) - } - for _, storable := range storables { - reports = append(reports, ConfigObjectReport{ - CfgObject: storable, - Err: nil, - }) - } - }) - - It("writes an acceptance status for each crd", func() { - err := rptr.WriteReports(reports) - Expect(err).NotTo(HaveOccurred()) - updatedSchemas, err := glooClient.V1().Schemas().List() - Expect(err).NotTo(HaveOccurred()) - Expect(updatedSchemas).To(HaveLen(len(schemas))) - for _, updatedSchema := range updatedSchemas { - Expect(updatedSchema.Status.State).To(Equal(gloov1.Status_Accepted)) - } - updatedvServices, err := glooClient.V1().ResolverMaps().List() - Expect(err).NotTo(HaveOccurred()) - Expect(updatedvServices).To(HaveLen(len(schemas))) - for _, updatedvService := range updatedvServices { - Expect(updatedvService.Status.State).To(Equal(gloov1.Status_Accepted)) - } - }) - }) - Context("writes status reports for cfg crds with SOME errors", func() { - BeforeEach(func() { - reports = nil - cfg, err := clientcmd.BuildConfigFromFlags(masterUrl, kubeconfigPath) - Expect(err).NotTo(HaveOccurred()) - glooClient, err = crd.NewStorage(cfg, namespace, time.Second) - Expect(err).NotTo(HaveOccurred()) - rptr = NewReporter(glooClient) - - testCfg := newTestConfig() - schemas = testCfg.Schemas - var storables []gloov1.ConfigObject - for _, us := range schemas { - _, err := glooClient.V1().Schemas().Create(us) - Expect(err).NotTo(HaveOccurred()) - storables = append(storables, us) - } - resolverMaps = testCfg.ResolverMaps - for _, vService := range resolverMaps { - _, err := glooClient.V1().ResolverMaps().Create(vService) - Expect(err).NotTo(HaveOccurred()) - storables = append(storables, vService) - } - for _, storable := range storables { - reports = append(reports, ConfigObjectReport{ - CfgObject: storable, - Err: errors.New("oh no an error what did u do!"), - }) - } - }) - - It("writes an rejected status for each crd", func() { - err := rptr.WriteReports(reports) - Expect(err).NotTo(HaveOccurred()) - updatedSchemas, err := glooClient.V1().Schemas().List() - Expect(err).NotTo(HaveOccurred()) - Expect(updatedSchemas).To(HaveLen(len(schemas))) - for _, updatedSchema := range updatedSchemas { - Expect(updatedSchema.Status.State).To(Equal(gloov1.Status_Rejected)) - } - updatedvServices, err := glooClient.V1().ResolverMaps().List() - Expect(err).NotTo(HaveOccurred()) - Expect(updatedvServices).To(HaveLen(len(schemas))) - for _, updatedvService := range updatedvServices { - Expect(updatedvService.Status.State).To(Equal(gloov1.Status_Rejected)) - } - }) - }) - }) -}) - -func newTestConfig() *v1.Config { - return &v1.Config{ - Schemas: []*v1.Schema{test.StarWarsV1Schema()}, - ResolverMaps: []*v1.ResolverMap{test.StarWarsResolverMap()}, - } -} diff --git a/pkg/reporter/interface.go b/pkg/reporter/interface.go deleted file mode 100644 index 5f63890..0000000 --- a/pkg/reporter/interface.go +++ /dev/null @@ -1,14 +0,0 @@ -package reporter - -import ( - "github.com/solo-io/gloo/pkg/api/types/v1" -) - -type ConfigObjectReport struct { - CfgObject v1.ConfigObject - Err error -} - -type Interface interface { - WriteReports(statuses []ConfigObjectReport) error -} diff --git a/pkg/reporter/reporter.go b/pkg/reporter/reporter.go deleted file mode 100644 index 846e7c0..0000000 --- a/pkg/reporter/reporter.go +++ /dev/null @@ -1,68 +0,0 @@ -package reporter - -import ( - "github.com/pkg/errors" - "github.com/solo-io/sqoop/pkg/storage" - - gloov1 "github.com/solo-io/gloo/pkg/api/types/v1" - "github.com/solo-io/gloo/pkg/log" - "github.com/solo-io/sqoop/pkg/api/types/v1" -) - -type reporter struct { - store storage.Interface -} - -func NewReporter(store storage.Interface) *reporter { - return &reporter{store: store} -} - -func (r *reporter) WriteReports(reports []ConfigObjectReport) error { - for _, report := range reports { - if err := r.writeReport(report); err != nil { - return errors.Wrapf(err, "failed to write report for config object %v", report.CfgObject) - } - log.Debugf("wrote report for %v", report.CfgObject.GetName()) - } - return nil -} - -func (r *reporter) writeReport(report ConfigObjectReport) error { - status := &gloov1.Status{ - State: gloov1.Status_Accepted, - } - if report.Err != nil { - status.State = gloov1.Status_Rejected - status.Reason = report.Err.Error() - } - name := report.CfgObject.GetName() - switch report.CfgObject.(type) { - case *v1.Schema: - schema, err := r.store.V1().Schemas().Get(report.CfgObject.GetName()) - if err != nil { - return errors.Wrapf(err, "failed to find schema %v", name) - } - // only update if status doesn't match - if schema.Status.Equal(status) { - return nil - } - schema.Status = status - if _, err := r.store.V1().Schemas().Update(schema); err != nil { - return errors.Wrapf(err, "failed to update schema with status report") - } - case *v1.ResolverMap: - resolverMap, err := r.store.V1().ResolverMaps().Get(name) - if err != nil { - return errors.Wrapf(err, "failed to find resolverMap %v", name) - } - // only update if status doesn't match - if resolverMap.Status.Equal(status) { - return nil - } - resolverMap.Status = status - if _, err := r.store.V1().ResolverMaps().Update(resolverMap); err != nil { - return errors.Wrapf(err, "failed to update resolverMap store with status report") - } - } - return nil -} diff --git a/pkg/reporter/reporter_suite_test.go b/pkg/reporter/reporter_suite_test.go deleted file mode 100644 index 26365e1..0000000 --- a/pkg/reporter/reporter_suite_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package reporter - -import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - "testing" - - "github.com/solo-io/gloo/pkg/log" -) - -func TestReporter(t *testing.T) { - RegisterFailHandler(Fail) - log.DefaultOut = GinkgoWriter - RunSpecs(t, "Reporter Suite") -} diff --git a/pkg/resolvers/gloo/gloo_suite_test.go b/pkg/resolvers/gloo/gloo_suite_test.go deleted file mode 100644 index 0ad6841..0000000 --- a/pkg/resolvers/gloo/gloo_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package gloo_test - -import ( - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -func TestGloo(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Gloo Suite") -} diff --git a/pkg/resolvers/template/template_suite_test.go b/pkg/resolvers/template/template_suite_test.go deleted file mode 100644 index d3ea3c9..0000000 --- a/pkg/resolvers/template/template_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package template_test - -import ( - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -func TestTemplate(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Template Suite") -} diff --git a/pkg/setup/setup.go b/pkg/setup/setup.go new file mode 100644 index 0000000..b5e04cf --- /dev/null +++ b/pkg/setup/setup.go @@ -0,0 +1,22 @@ +package setup + +import ( + "time" + + "github.com/solo-io/gloo/pkg/utils/setuputils" + + check "github.com/solo-io/go-checkpoint" + + "github.com/solo-io/sqoop/pkg/syncer" + "github.com/solo-io/sqoop/version" +) + +func Main() error { + start := time.Now() + check.CallCheck("sqoop", version.Version, start) + return setuputils.Main(setuputils.SetupOpts{ + SetupFunc: syncer.Setup, + ExitOnError: true, + LoggingPrefix: "sqoop", + }) +} diff --git a/pkg/sqoopctl/install/docker.go b/pkg/sqoopctl/install/docker.go deleted file mode 100644 index 0b5cfc5..0000000 --- a/pkg/sqoopctl/install/docker.go +++ /dev/null @@ -1,160 +0,0 @@ -package install - -import ( - "fmt" - "os" - "path/filepath" - - "io" - "net/http" - - "github.com/pkg/errors" - "github.com/solo-io/gloo/pkg/bootstrap" - "github.com/solo-io/glooctl/pkg/config" - "github.com/spf13/cobra" -) - -const ( - successMessage = `Sqoop installed successfully. -Please switch to directory '%s', and run "docker-compose up" -to start Sqoop. - -` -) - -var installDockerCmd = &cobra.Command{ - Use: "docker [folder]", - Short: "install Sqoop with Docker and file-based storage", - Long: ` -Installs gloo to run with Docker Compose in the given install folder. -If the folder doesn't exist glooctl will create it. - -Once installed you can go to the install folder and run: - docker-compose up -to start gloo. - -Glooctl will configure itself to use this instance of gloo.`, - Args: cobra.ExactArgs(1), - Run: func(c *cobra.Command, a []string) { - pwd, err := os.Getwd() - if err != nil { - fmt.Println("Unable to get current directory", err) - os.Exit(1) - } - installDir := filepath.Join(pwd, a[0]) - err = dockerInstall(installDir) - if err != nil { - fmt.Printf("Unable to install gloo to %s: %q\n", installDir, err) - os.Exit(1) - } - fmt.Printf(successMessage, installDir) - }, -} - -func init() { - installCmd.AddCommand(installDockerCmd) -} - -const ( - envoyYamlURL = "https://raw.githubusercontent.com/solo-io/sqoop/master/install/docker-compose/envoy-config.yaml" - dockerComposeYamlURL = "https://raw.githubusercontent.com/solo-io/sqoop/master/install/docker-compose/docker-compose.yaml" -) - -func dockerInstall(folder string) error { - err := createInstallFolder(folder) - if err != nil { - return err - } - err = download(dockerComposeYamlURL, filepath.Join(folder, "docker-compose.yaml")) - if err != nil { - return err - } - err = download(envoyYamlURL, filepath.Join(folder, "envoy-config.yaml")) - if err != nil { - return err - } - - err = createStorageFolders(folder) - if err != nil { - return err - } - - return updateSqoopctlConfig(folder) -} - -func createInstallFolder(folder string) error { - stat, err := os.Stat(folder) - if err != nil { - if !os.IsNotExist(err) { - return errors.Wrap(err, "unable to setup install directory") - } - err = os.MkdirAll(folder, 0755) - if err != nil { - return errors.Wrap(err, "unable to create directory") - } - return nil - } - - if !stat.IsDir() { - return errors.Errorf("%s already exists and isn't a directory", folder) - } - - return nil -} - -func createStorageFolders(folder string) error { - // _gloo_config/* - for _, f := range []string{ - "upstreams", - "virtualservices", - "roles", - "secrets", - "files", - "schemas", - "resolvermaps", - } { - err := os.MkdirAll(filepath.Join(folder, "_gloo_config", f), 0755) - if err != nil { - return errors.Wrap(err, "unable to create storage directory"+f) - } - } - - return nil -} - -func updateSqoopctlConfig(folder string) error { - opts := &bootstrap.Options{} - - opts.ConfigStorageOptions.Type = "file" - opts.FileStorageOptions.Type = "file" - opts.SecretStorageOptions.Type = "file" - - opts.FileOptions.ConfigDir = filepath.Join(folder, "_gloo_config") - opts.FileOptions.FilesDir = filepath.Join(folder, "_gloo_config/files") - opts.FileOptions.SecretDir = filepath.Join(folder, "_gloo_config/secrets") - - err := config.SaveConfig(opts) - if err != nil { - return errors.Wrap(err, "unable to configure glooctl") - } - - return nil -} - -func download(src, dst string) error { - f, err := os.Create(dst) - if err != nil { - return err - } - defer f.Close() - resp, err := http.Get(src) - if err != nil { - return err - } - if resp.StatusCode != 200 { - return errors.Wrapf(err, "status code %v", resp.StatusCode) - } - defer resp.Body.Close() - _, err = io.Copy(f, resp.Body) - return err -} diff --git a/pkg/sqoopctl/install/kube.go b/pkg/sqoopctl/install/kube.go deleted file mode 100644 index b2484f2..0000000 --- a/pkg/sqoopctl/install/kube.go +++ /dev/null @@ -1,52 +0,0 @@ -package install - -import ( - "fmt" - "os" - - "os/exec" - - "github.com/spf13/cobra" -) - -const sqoopYamlURI = "https://raw.githubusercontent.com/solo-io/sqoop/master/install/kube/install.yaml" - -var installKubeCmd = &cobra.Command{ - Use: "kube", - Short: "install Sqoop on Kubernetes", - Long: ` - Installs latest Sqoop into a Kubernetes cluster. It downloads the latest installation YAML - file and installs to the current kubectl context.`, - Run: func(c *cobra.Command, a []string) { - err := kubeInstall(dryRun, sqoopYamlURI) - if err != nil { - fmt.Printf("Unable to isntall Sqoop to Kubernetes %q\n", err) - os.Exit(1) - } - if !dryRun { - fmt.Println("Sqoop successfully installed.") - } - }, -} - -var dryRun bool - -func init() { - installKubeCmd.PersistentFlags().BoolVarP(&dryRun, "dry-run", "d", false, "If true, only "+ - "print the objects that will be installed") - installCmd.AddCommand(installKubeCmd) -} - -// Install setups Gloo on Kubernetes using kubectl and current context -func kubeInstall(dryRun bool, uri string) error { - // using kubectl with latest install.yaml - args := []string{"apply", "--filename", - uri} - if dryRun { - args = append(args, "--dry-run=true") - } - cmd := exec.Command("kubectl", args...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - return cmd.Run() -} diff --git a/pkg/sqoopctl/install/root.go b/pkg/sqoopctl/install/root.go deleted file mode 100644 index 4797fcc..0000000 --- a/pkg/sqoopctl/install/root.go +++ /dev/null @@ -1,16 +0,0 @@ -package install - -import ( - "github.com/solo-io/sqoop/pkg/sqoopctl" - "github.com/spf13/cobra" -) - -var installCmd = &cobra.Command{ - Use: "install", - Short: "Install Sqoop and dependencies to supported environments", - Long: `sqoopctl currently suppports installations using docker-compose and Kubernetes`, -} - -func init() { - sqoopctl.RootCmd.AddCommand(installCmd) -} diff --git a/pkg/sqoopctl/resolvermap/resolvermap_create.go b/pkg/sqoopctl/resolvermap/resolvermap_create.go deleted file mode 100644 index 869b39b..0000000 --- a/pkg/sqoopctl/resolvermap/resolvermap_create.go +++ /dev/null @@ -1,55 +0,0 @@ -package resolvermap - -import ( - "fmt" - - "github.com/pkg/errors" - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/sqoopctl" - "github.com/solo-io/sqoop/pkg/storage/file" - "github.com/spf13/cobra" -) - -var resolverMapCreateOpts struct { - FromFile string -} - -var resolverMapCreateCmd = &cobra.Command{ - Use: "create NAME --from-file ", - Short: "upload a resolver map to Sqoop from a local Sqoop ResolverMap yaml file", - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) != 1 { - return errors.Errorf("requires exactly 1 argument") - } - if err := createResolverMap(args[0], resolverMapCreateOpts.FromFile); err != nil { - return err - } - fmt.Println("resolver map created successfully") - return nil - }, -} - -func init() { - resolverMapCreateCmd.PersistentFlags().StringVarP(&resolverMapCreateOpts.FromFile, "from-file", "f", "", "path to a "+ - "graphql resolver map file from which to create the Sqoop resolver map object") - resolverMapCmd.AddCommand(resolverMapCreateCmd) -} - -func createResolverMap(name, filename string) error { - if name == "" { - return errors.Errorf("resolver map name must be set") - } - if filename == "" { - return errors.Errorf("filename must be set") - } - cli, err := sqoopctl.MakeClient() - if err != nil { - return err - } - var resolverMap v1.ResolverMap - if err := file.ReadFileInto(filename, &resolverMap); err != nil { - return err - } - _, err = cli.V1().ResolverMaps().Create(&resolverMap) - return err -} diff --git a/pkg/sqoopctl/resolvermap/resolvermap_delete.go b/pkg/sqoopctl/resolvermap/resolvermap_delete.go deleted file mode 100644 index 0815ae2..0000000 --- a/pkg/sqoopctl/resolvermap/resolvermap_delete.go +++ /dev/null @@ -1,37 +0,0 @@ -package resolvermap - -import ( - "fmt" - - "github.com/pkg/errors" - "github.com/solo-io/sqoop/pkg/sqoopctl" - "github.com/spf13/cobra" -) - -var resolverMapDeleteCmd = &cobra.Command{ - Use: "delete [NAME]", - Short: "delete a resolver map by its name", - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) != 1 || args[0] == "" { - return errors.Errorf("must provide name") - } - err := deleteResolverMap(args[0]) - if err != nil { - return err - } - fmt.Println("delete succesful") - return nil - }, -} - -func init() { - resolverMapCmd.AddCommand(resolverMapDeleteCmd) -} - -func deleteResolverMap(name string) error { - cli, err := sqoopctl.MakeClient() - if err != nil { - return err - } - return cli.V1().ResolverMaps().Delete(name) -} diff --git a/pkg/sqoopctl/resolvermap/resolvermap_get.go b/pkg/sqoopctl/resolvermap/resolvermap_get.go deleted file mode 100644 index d79db04..0000000 --- a/pkg/sqoopctl/resolvermap/resolvermap_get.go +++ /dev/null @@ -1,51 +0,0 @@ -package resolvermap - -import ( - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/sqoopctl" - "github.com/spf13/cobra" -) - -var resolverMapGetCmd = &cobra.Command{ - Use: "get [NAME]", - Short: "return a resolver map by its name or list all resolver maps", - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) != 1 || args[0] == "" { - list, err := listResolverMaps() - if err != nil { - return err - } - for _, msg := range list { - if err := sqoopctl.Print(msg); err != nil { - return err - } - } - return nil - } - msg, err := getResolverMap(args[0]) - if err != nil { - return err - } - return sqoopctl.Print(msg) - }, -} - -func init() { - resolverMapCmd.AddCommand(resolverMapGetCmd) -} - -func getResolverMap(name string) (*v1.ResolverMap, error) { - cli, err := sqoopctl.MakeClient() - if err != nil { - return nil, err - } - return cli.V1().ResolverMaps().Get(name) -} - -func listResolverMaps() ([]*v1.ResolverMap, error) { - cli, err := sqoopctl.MakeClient() - if err != nil { - return nil, err - } - return cli.V1().ResolverMaps().List() -} diff --git a/pkg/sqoopctl/resolvermap/resolvermap_register.go b/pkg/sqoopctl/resolvermap/resolvermap_register.go deleted file mode 100644 index 107f2aa..0000000 --- a/pkg/sqoopctl/resolvermap/resolvermap_register.go +++ /dev/null @@ -1,106 +0,0 @@ -package resolvermap - -import ( - "github.com/pkg/errors" - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/sqoopctl" - "github.com/solo-io/sqoop/pkg/storage" - "github.com/spf13/cobra" -) - -var ( - schemaName string - functionName string - upstreamName string - requestTemplate string - responseTemplate string -) - -var resolverMapRegisterCmd = &cobra.Command{ - Use: "register TypeName FieldName -f resolver.yaml [-s schema-name]", - Short: "Register a resolver for a field in your Schema", - Long: `Sets the resolver for a field in your schema. TypeName.FieldName will always be resolved using this resolver - -Resolvers must be defined in yaml format. See the documentation at https://sqoop.solo.io/v1/resolver_map/#sqoop.api.v1.Resolver for the API specification for Sqoop Resolvers`, - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) != 2 || args[0] == "" || args[1] == "" { - return errors.Errorf("must specify args TypeName and FieldName") - } - if upstreamName == "" || functionName == "" { - return errors.Errorf("must provide an upstream and function to create a resolver") - } - msg, err := registerResolver(schemaName, args[0], args[1], upstreamName, functionName, requestTemplate, responseTemplate) - if err != nil { - return err - } - return sqoopctl.Print(msg) - }, -} - -func init() { - resolverMapRegisterCmd.PersistentFlags().StringVarP(&upstreamName, "upstream", "u", "", "upstream where the function lives") - resolverMapRegisterCmd.PersistentFlags().StringVarP(&functionName, "function", "f", "", "function to use as resolver") - resolverMapRegisterCmd.PersistentFlags().StringVarP(&requestTemplate, "request-template", "b", "", "template to use for the request body") - resolverMapRegisterCmd.PersistentFlags().StringVarP(&responseTemplate, "response-template", "r", "", "template to use for the response body") - resolverMapRegisterCmd.PersistentFlags().StringVarP(&schemaName, "schema", "s", "", "name of the "+ - "schema to connect this resolver to. this is required if more than one schema contains a definition for the "+ - "type name.") - resolverMapCmd.AddCommand(resolverMapRegisterCmd) -} - -func registerResolver(schemaName, typeName, fieldName, upstreamName, functionName, requestTemplate, responseTemplate string) (*v1.ResolverMap, error) { - resolver := &v1.Resolver{ - Resolver: &v1.Resolver_GlooResolver{ - GlooResolver: &v1.GlooResolver{ - RequestTemplate: requestTemplate, - ResponseTemplate: responseTemplate, - Function: &v1.GlooResolver_SingleFunction{ - SingleFunction: &v1.Function{ - Upstream: upstreamName, - Function: functionName, - }, - }, - }, - }, - } - cli, err := sqoopctl.MakeClient() - if err != nil { - return nil, err - } - existingResolverMap, err := getResolverMapFor(cli, schemaName, typeName, fieldName) - if err != nil { - return nil, err - } - - existingResolverMap.Types[typeName].Fields[fieldName] = resolver - - return cli.V1().ResolverMaps().Update(existingResolverMap) -} - -func getResolverMapFor(cli storage.Interface, schemaName, typeName, fieldName string) (*v1.ResolverMap, error) { - if schemaName != "" { - schema, err := cli.V1().Schemas().Get(schemaName) - if err != nil { - return nil, err - } - if schema.ResolverMap == "" { - return nil, errors.Errorf("schema %v does not have a resolver map defined", schemaName) - } - return cli.V1().ResolverMaps().Get(schema.ResolverMap) - } - resolverMaps, err := cli.V1().ResolverMaps().List() - if err != nil { - return nil, err - } - for _, rm := range resolverMaps { - typResolver, ok := rm.Types[typeName] - if !ok { - continue - } - if _, ok := typResolver.Fields[fieldName]; !ok { - continue - } - return rm, nil - } - return nil, errors.Errorf("cannot find a resolver map for type %v with field %v", typeName, fieldName) -} diff --git a/pkg/sqoopctl/resolvermap/resolvermap_update.go b/pkg/sqoopctl/resolvermap/resolvermap_update.go deleted file mode 100644 index e013938..0000000 --- a/pkg/sqoopctl/resolvermap/resolvermap_update.go +++ /dev/null @@ -1,68 +0,0 @@ -package resolvermap - -import ( - "fmt" - - "github.com/pkg/errors" - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/sqoopctl" - "github.com/solo-io/sqoop/pkg/storage/file" - "github.com/spf13/cobra" -) - -var resolverMapUpdateOpts struct { - FromFile string -} - -var resolverMapUpdateCmd = &cobra.Command{ - Use: "update NAME --from-file ", - Short: "upload a resolver map to Sqoop from a local Sqoop ResolverMap yaml file", - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) != 1 { - return errors.Errorf("requires exactly 1 argument") - } - if err := updateResolverMap(args[0], resolverMapUpdateOpts.FromFile); err != nil { - return err - } - fmt.Println("resolver map updated successfully") - return nil - }, -} - -func init() { - resolverMapUpdateCmd.PersistentFlags().StringVarP(&resolverMapUpdateOpts.FromFile, "from-file", "f", "", "path to a "+ - "graphql resolver map file from which to update the Sqoop resolver map object") - resolverMapCmd.AddCommand(resolverMapUpdateCmd) -} - -func updateResolverMap(name, filename string) error { - cli, err := sqoopctl.MakeClient() - if err != nil { - return err - } - if name == "" { - return errors.Errorf("schema name must be set") - } - if filename == "" { - return errors.Errorf("filename must be set") - } - - existing, err := cli.V1().ResolverMaps().Get(name) - if err != nil { - return err - } - - if name == "" { - return errors.Errorf("resolver map name must be set") - } - if filename == "" { - return errors.Errorf("filename must be set") - } - var resolverMap v1.ResolverMap - if err := file.ReadFileInto(filename, &resolverMap); err != nil { - return err - } - resolverMap.Metadata = existing.Metadata - _, err = cli.V1().ResolverMaps().Update(&resolverMap) - return err -} diff --git a/pkg/sqoopctl/resolvermap/root.go b/pkg/sqoopctl/resolvermap/root.go deleted file mode 100644 index 280e67b..0000000 --- a/pkg/sqoopctl/resolvermap/root.go +++ /dev/null @@ -1,19 +0,0 @@ -package resolvermap - -import ( - "github.com/solo-io/sqoop/pkg/sqoopctl" - "github.com/spf13/cobra" -) - -var resolverMapCmd = &cobra.Command{ - Use: "resolvermap", - Aliases: []string{ - "resolvermaps", "rms", - }, - Short: "Create, read, update, and delete Sqoop Resolver Maps", - Long: `Use these commands to define resolvers for your GraphQL Schemas`, -} - -func init() { - sqoopctl.RootCmd.AddCommand(resolverMapCmd) -} diff --git a/pkg/sqoopctl/root.go b/pkg/sqoopctl/root.go deleted file mode 100644 index b007ef6..0000000 --- a/pkg/sqoopctl/root.go +++ /dev/null @@ -1,102 +0,0 @@ -package sqoopctl - -import ( - "fmt" - - "strings" - - "github.com/ghodss/yaml" - "github.com/gogo/protobuf/proto" - "github.com/pkg/errors" - "github.com/solo-io/gloo/pkg/bootstrap" - glooflags "github.com/solo-io/gloo/pkg/bootstrap/flags" - "github.com/solo-io/gloo/pkg/protoutil" - "github.com/solo-io/glooctl/pkg/config" - "github.com/solo-io/sqoop/pkg/api/types/v1" - sqoopstorage "github.com/solo-io/sqoop/pkg/bootstrap" - "github.com/solo-io/sqoop/pkg/storage" - "github.com/spf13/cobra" -) - -var Opts bootstrap.Options - -var outputFormat string - -var RootCmd = &cobra.Command{ - Use: "sqoopctl", - Short: "Interact with Sqoop's storage API from the command line", - Long: "As Sqoop features a storage-based API, direct communication with " + - "the Sqoop server is not necessary. sqoopctl simplifies the administration of " + - "Sqoop by providing an easy way to create, read, update, and delete Sqoop storage objects.\n\n" + - "" + - "The primary concerns of sqoopctl are Schemas and ResolverMaps. Schemas contain your GraphQL schema;" + - " ResolverMaps define how your schema fields are resolved.\n\n" + - "" + - "Start by creating a schema using sqoopctl schema create --from-file ", -} - -func init() { - RootCmd.PersistentFlags().StringVarP(&outputFormat, "output", "o", "", "output format for results") - glooflags.AddConfigStorageOptionFlags(RootCmd, &Opts) - glooflags.AddFileFlags(RootCmd, &Opts) - glooflags.AddKubernetesFlags(RootCmd, &Opts) - glooflags.AddConsulFlags(RootCmd, &Opts) - config.LoadConfig(&Opts) -} - -func printAsYaml(msg proto.Message) error { - jsn, err := protoutil.Marshal(msg) - if err != nil { - return err - } - yam, err := yaml.JSONToYAML(jsn) - if err != nil { - return err - } - fmt.Printf("%s\n", yam) - return nil -} - -func printAsJSON(msg proto.Message) error { - jsn, err := protoutil.Marshal(msg) - if err != nil { - return err - } - fmt.Printf("%s\n", jsn) - return nil -} - -func printTable(msg proto.Message) error { - switch obj := msg.(type) { - case *v1.Schema: - printSchema(obj) - case *v1.ResolverMap: - printResolverMap(obj) - default: - return errors.Errorf("unknown type %v", msg) - } - return nil -} - -func printSchema(schema *v1.Schema) { - fmt.Printf("%v", schema.Name) -} - -func printResolverMap(resolverMap *v1.ResolverMap) { - fmt.Printf("%v", resolverMap.Name) -} - -func Print(msg proto.Message) error { - switch strings.ToLower(outputFormat) { - case "yaml": - return printAsYaml(msg) - case "json": - return printAsJSON(msg) - default: - return printTable(msg) - } -} - -func MakeClient() (storage.Interface, error) { - return sqoopstorage.Bootstrap(Opts) -} diff --git a/pkg/sqoopctl/schema/root.go b/pkg/sqoopctl/schema/root.go deleted file mode 100644 index 746d3c7..0000000 --- a/pkg/sqoopctl/schema/root.go +++ /dev/null @@ -1,19 +0,0 @@ -package schema - -import ( - "github.com/solo-io/sqoop/pkg/sqoopctl" - "github.com/spf13/cobra" -) - -var schemaCmd = &cobra.Command{ - Use: "schema", - Aliases: []string{ - "schemas", "s", - }, - Short: "Create, read, update, and delete GraphQL schemas for Sqoop", - Long: `Use these commands to register a GraphQL schema with Sqoop`, -} - -func init() { - sqoopctl.RootCmd.AddCommand(schemaCmd) -} diff --git a/pkg/sqoopctl/schema/schema_create.go b/pkg/sqoopctl/schema/schema_create.go deleted file mode 100644 index d39b9eb..0000000 --- a/pkg/sqoopctl/schema/schema_create.go +++ /dev/null @@ -1,64 +0,0 @@ -package schema - -import ( - "fmt" - "io/ioutil" - - "github.com/pkg/errors" - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/sqoopctl" - "github.com/spf13/cobra" -) - -var schemaCreateOpts struct { - FromFile string - UseResolverMap string -} - -var schemaCreateCmd = &cobra.Command{ - Use: "create NAME --from-file ", - Short: "upload a schema to Sqoop from a local GraphQL Schema file", - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) != 1 { - return errors.Errorf("requires exactly 1 argument") - } - if err := createSchema(args[0], schemaCreateOpts.FromFile, schemaCreateOpts.UseResolverMap); err != nil { - return err - } - fmt.Println("schema created successfully") - return nil - }, -} - -func init() { - schemaCreateCmd.PersistentFlags().StringVarP(&schemaCreateOpts.FromFile, "from-file", "f", "", "path to a "+ - "graphql schema file from which to create the Sqoop schema object") - schemaCreateCmd.PersistentFlags().StringVarP(&schemaCreateOpts.UseResolverMap, "resolvermap", "r", "", "The name of a "+ - "ResolverMap to connect to this Schema. If none is specified, an empty ResolverMap will be generated for you, which "+ - "you can then configure with sqoopctl") - schemaCmd.AddCommand(schemaCreateCmd) -} - -func createSchema(name, filename, resolvermap string) error { - if name == "" { - return errors.Errorf("schema name must be set") - } - if filename == "" { - return errors.Errorf("filename must be set") - } - cli, err := sqoopctl.MakeClient() - if err != nil { - return err - } - inlineSchemaBytes, err := ioutil.ReadFile(filename) - if err != nil { - return err - } - schema := &v1.Schema{ - Name: name, - InlineSchema: string(inlineSchemaBytes), - ResolverMap: resolvermap, - } - _, err = cli.V1().Schemas().Create(schema) - return err -} diff --git a/pkg/sqoopctl/schema/schema_delete.go b/pkg/sqoopctl/schema/schema_delete.go deleted file mode 100644 index 5943a09..0000000 --- a/pkg/sqoopctl/schema/schema_delete.go +++ /dev/null @@ -1,37 +0,0 @@ -package schema - -import ( - "fmt" - - "github.com/solo-io/sqoop/pkg/sqoopctl" - "github.com/spf13/cobra" - "github.com/vektah/gqlgen/neelance/errors" -) - -var schemaDeleteCmd = &cobra.Command{ - Use: "delete [NAME]", - Short: "delete a schema by its name", - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) != 1 || args[0] == "" { - return errors.Errorf("must provide name") - } - err := deleteSchema(args[0]) - if err != nil { - return err - } - fmt.Println("delete succesful") - return nil - }, -} - -func init() { - schemaCmd.AddCommand(schemaDeleteCmd) -} - -func deleteSchema(name string) error { - cli, err := sqoopctl.MakeClient() - if err != nil { - return err - } - return cli.V1().Schemas().Delete(name) -} diff --git a/pkg/sqoopctl/schema/schema_get.go b/pkg/sqoopctl/schema/schema_get.go deleted file mode 100644 index 474cc01..0000000 --- a/pkg/sqoopctl/schema/schema_get.go +++ /dev/null @@ -1,51 +0,0 @@ -package schema - -import ( - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/sqoopctl" - "github.com/spf13/cobra" -) - -var schemaGetCmd = &cobra.Command{ - Use: "get [NAME]", - Short: "return a schema by its name or list all schemas", - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) != 1 || args[0] == "" { - list, err := listSchemas() - if err != nil { - return err - } - for _, msg := range list { - if err := sqoopctl.Print(msg); err != nil { - return err - } - } - return nil - } - msg, err := getSchema(args[0]) - if err != nil { - return err - } - return sqoopctl.Print(msg) - }, -} - -func init() { - schemaCmd.AddCommand(schemaGetCmd) -} - -func getSchema(name string) (*v1.Schema, error) { - cli, err := sqoopctl.MakeClient() - if err != nil { - return nil, err - } - return cli.V1().Schemas().Get(name) -} - -func listSchemas() ([]*v1.Schema, error) { - cli, err := sqoopctl.MakeClient() - if err != nil { - return nil, err - } - return cli.V1().Schemas().List() -} diff --git a/pkg/sqoopctl/schema/schema_update.go b/pkg/sqoopctl/schema/schema_update.go deleted file mode 100644 index 47febf1..0000000 --- a/pkg/sqoopctl/schema/schema_update.go +++ /dev/null @@ -1,71 +0,0 @@ -package schema - -import ( - "fmt" - "io/ioutil" - - "github.com/pkg/errors" - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/sqoopctl" - "github.com/spf13/cobra" -) - -var schemaUpdateOpts struct { - FromFile string - UseResolverMap string -} - -var schemaUpdateCmd = &cobra.Command{ - Use: "update NAME --from-file ", - Short: "upload a schema to Sqoop from a local GraphQL Schema file", - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) != 1 { - return errors.Errorf("requires exactly 1 argument") - } - if err := updateSchema(args[0], schemaUpdateOpts.FromFile, schemaUpdateOpts.UseResolverMap); err != nil { - return err - } - fmt.Println("schema updated successfully") - return nil - }, -} - -func init() { - schemaUpdateCmd.PersistentFlags().StringVarP(&schemaUpdateOpts.FromFile, "from-file", "f", "", "path to a "+ - "graphql schema file from which to update the Sqoop schema object") - schemaUpdateCmd.PersistentFlags().StringVarP(&schemaUpdateOpts.UseResolverMap, "resolvermap", "r", "", "The name of a "+ - "ResolverMap to connect to this Schema. If none is specified, an empty ResolverMap will be generated for you, which "+ - "you can then configure with sqoopctl") - schemaCmd.AddCommand(schemaUpdateCmd) -} - -func updateSchema(name, filename, resolvermap string) error { - cli, err := sqoopctl.MakeClient() - if err != nil { - return err - } - if name == "" { - return errors.Errorf("schema name must be set") - } - if filename == "" { - return errors.Errorf("filename must be set") - } - - existing, err := cli.V1().Schemas().Get(name) - if err != nil { - return err - } - - inlineSchemaBytes, err := ioutil.ReadFile(filename) - if err != nil { - return err - } - schema := &v1.Schema{ - Name: name, - InlineSchema: string(inlineSchemaBytes), - ResolverMap: resolvermap, - Metadata: existing.Metadata, - } - _, err = cli.V1().Schemas().Update(schema) - return err -} diff --git a/pkg/storage/README.md b/pkg/storage/README.md deleted file mode 100644 index 198bc42..0000000 --- a/pkg/storage/README.md +++ /dev/null @@ -1,10 +0,0 @@ -Storage Client ----- - -This repository contains the sources for the Sqoop Storage Client. The Sqoop Storage Client is the centerpiece for the Sqoop -API. Operators for Sqoop and Sqoop itself consume this library to interact with the storage layer, the universal source of truth in Sqoop's world. - -Developers of integrations should use this repository as a client library for their application (if it's written in Go). -Support for more languages is in our roadmap. - -For information about storage in Sqoop and writing client integrations, see our [documentation](https://sqoop.solo.io). diff --git a/pkg/storage/base/base_client.go b/pkg/storage/base/base_client.go deleted file mode 100644 index 3db89e6..0000000 --- a/pkg/storage/base/base_client.go +++ /dev/null @@ -1,185 +0,0 @@ -package base - -import ( - "github.com/pkg/errors" - - "github.com/hashicorp/consul/api" - "github.com/solo-io/gloo/pkg/log" - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/storage" -) - -// TODO: evaluate efficiency of LSing a whole dir on every op -// so far this is preferable to caring what files are named -type ConsulStorageClient struct { - rootPath string - consul *api.Client -} - -func NewConsulStorageClient(rootPath string, consul *api.Client) *ConsulStorageClient { - return &ConsulStorageClient{ - rootPath: rootPath, - consul: consul, - } -} - -func (c *ConsulStorageClient) Create(item *StorableItem) (*StorableItem, error) { - p, err := toKVPair(c.rootPath, item) - if err != nil { - return nil, errors.Wrapf(err, "converting %s to kv pair", item.GetName()) - } - - // error if the key already exists - existingP, _, err := c.consul.KV().Get(p.Key, &api.QueryOptions{RequireConsistent: true}) - if err != nil { - return nil, errors.Wrap(err, "failed to query consul") - } - if existingP != nil { - return nil, storage.NewAlreadyExistsErr( - errors.Errorf("key found for storageItem %s: %s", item.GetName(), p.Key)) - } - - // create the item - if _, err := c.consul.KV().Put(p, nil); err != nil { - return nil, errors.Wrapf(err, "writing kv pair %s", p.Key) - } - cfgObject, err := c.Get(item.GetName()) - if err != nil { - return nil, errors.Wrapf(err, "getting newly created cfg object %s", p.Key) - } - return cfgObject, nil -} - -func (c *ConsulStorageClient) Update(item *StorableItem) (*StorableItem, error) { - updatedP, err := toKVPair(c.rootPath, item) - if err != nil { - return nil, errors.Wrapf(err, "converting %s to kv pair", item.GetName()) - } - - // error if the key doesn't already exist - existingP, _, err := c.consul.KV().Get(updatedP.Key, &api.QueryOptions{RequireConsistent: true}) - if err != nil { - return nil, errors.Wrap(err, "failed to query consul") - } - if existingP == nil { - return nil, errors.Errorf("key not found for storageItem %s: %s", item.GetName(), updatedP.Key) - } - - // update the item - if success, _, err := c.consul.KV().CAS(updatedP, nil); err != nil { - return nil, errors.Wrapf(err, "writing kv pair %s", updatedP.Key) - } else if !success { - return nil, errors.Errorf("resource version was invalid for storageItem: %s", item.GetName()) - } - - cfgObject, err := c.Get(item.GetName()) - if err != nil { - return nil, errors.Wrapf(err, "getting updated created cfg object %s", existingP.Key) - } - return cfgObject, nil -} - -func (c *ConsulStorageClient) Delete(name string) error { - key := key(c.rootPath, name) - - _, err := c.consul.KV().Delete(key, nil) - if err != nil { - return errors.Wrapf(err, "deleting %s", name) - } - return nil -} - -func (c *ConsulStorageClient) Get(name string) (*StorableItem, error) { - key := key(c.rootPath, name) - p, _, err := c.consul.KV().Get(key, nil) - if err != nil { - return nil, errors.Wrapf(err, "getting pair for for key %v", key) - } - if p == nil { - return nil, errors.Errorf("keypair %s not found for storageItem %s", key, name) - } - obj, err := itemFromKVPair(p) - if err != nil { - return nil, errors.Wrap(err, "converting consul kv-pair to storageItem") - } - return obj, nil -} - -func (c *ConsulStorageClient) List() ([]*StorableItem, error) { - pairs, _, err := c.consul.KV().List(c.rootPath, &api.QueryOptions{RequireConsistent: true}) - if err != nil { - return nil, errors.Wrapf(err, "listing key-value pairs for root %s", c.rootPath) - } - var storageItems []*StorableItem - for _, p := range pairs { - obj, err := itemFromKVPair(p) - if err != nil { - return nil, errors.Wrapf(err, "converting %s to storageItem", p.Key) - } - storageItems = append(storageItems, obj) - } - return storageItems, nil -} - -// TODO: be clear that watch for consul only calls update -func (c *ConsulStorageClient) Watch(handlers ...StorableItemEventHandler) (*storage.Watcher, error) { - var lastIndex uint64 - sync := func() error { - pairs, meta, err := c.consul.KV().List(c.rootPath, &api.QueryOptions{RequireConsistent: true, WaitIndex: lastIndex}) - if err != nil { - return errors.Wrap(err, "getting kv-pairs list") - } - // no change since last poll - if lastIndex == meta.LastIndex { - return nil - } - var ( - resolverMaps []*v1.ResolverMap - schemas []*v1.Schema - ) - for _, p := range pairs { - item, err := itemFromKVPair(p) - if err != nil { - return errors.Wrapf(err, "converting %s to storageItem", p.Key) - } - - switch { - case item.Schema != nil: - schemas = append(schemas, item.Schema) - case item.ResolverMap != nil: - resolverMaps = append(resolverMaps, item.ResolverMap) - default: - panic("virtual service, role, file or schema must be set") - - } - } - // update index - lastIndex = meta.LastIndex - switch { - case len(schemas) > 0: - for _, h := range handlers { - h.SchemaEventHandler.OnUpdate(schemas, nil) - } - case len(resolverMaps) > 0: - for _, h := range handlers { - h.ResolverMapEventHandler.OnUpdate(resolverMaps, nil) - } - } - return nil - } - return storage.NewWatcher(func(stop <-chan struct{}, errs chan error) { - for { - select { - default: - if err := sync(); err != nil { - log.Warnf("error syncing with consul kv-pairs: %v", err) - } - case err := <-errs: - log.Warnf("failed to start watcher to: %v", err) - return - case <-stop: - return - } - } - }), nil -} diff --git a/pkg/storage/base/conversion.go b/pkg/storage/base/conversion.go deleted file mode 100644 index 99a0259..0000000 --- a/pkg/storage/base/conversion.go +++ /dev/null @@ -1,61 +0,0 @@ -package base - -import ( - "fmt" - "strconv" - - "github.com/gogo/protobuf/proto" - "github.com/hashicorp/consul/api" - "github.com/pkg/errors" - "github.com/solo-io/sqoop/pkg/api/types/v1" -) - -func key(rootPath, itemName string) string { - return rootPath + "/" + itemName -} - -func toKVPair(rootPath string, item *StorableItem) (*api.KVPair, error) { - data, err := item.GetBytes() - if err != nil { - return nil, errors.Wrap(err, "getting bytes to store") - } - var modifyIndex uint64 - if item.GetResourceVersion() != "" { - if i, err := strconv.Atoi(item.GetResourceVersion()); err == nil { - modifyIndex = uint64(i) - } - } - return &api.KVPair{ - Key: key(rootPath, item.GetName()), - Value: data, - Flags: uint64(item.GetTypeFlag()), - ModifyIndex: modifyIndex, - }, nil -} - -func setResourceVersion(item *StorableItem, p *api.KVPair) { - resourceVersion := fmt.Sprintf("%v", p.ModifyIndex) - item.SetResourceVersion(resourceVersion) -} - -func itemFromKVPair(p *api.KVPair) (*StorableItem, error) { - item := &StorableItem{} - switch StorableItemType(p.Flags) { - case StorableItemTypeSchema: - var schema v1.Schema - err := proto.Unmarshal(p.Value, &schema) - if err != nil { - return nil, errors.Wrap(err, "unmarshalling value as schema") - } - item.Schema = &schema - case StorableItemTypeResolverMap: - var resolverMap v1.ResolverMap - err := proto.Unmarshal(p.Value, &resolverMap) - if err != nil { - return nil, errors.Wrap(err, "unmarshalling value as virtual service") - } - item.ResolverMap = &resolverMap - } - setResourceVersion(item, p) - return item, nil -} diff --git a/pkg/storage/base/storable_item.go b/pkg/storage/base/storable_item.go deleted file mode 100644 index 8f3c117..0000000 --- a/pkg/storage/base/storable_item.go +++ /dev/null @@ -1,92 +0,0 @@ -package base - -import ( - "github.com/gogo/protobuf/proto" - gloov1 "github.com/solo-io/gloo/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/storage" -) - -type StorableItem struct { - Schema *v1.Schema - ResolverMap *v1.ResolverMap -} - -func (item *StorableItem) GetName() string { - switch { - case item.Schema != nil: - return item.Schema.GetName() - case item.ResolverMap != nil: - return item.ResolverMap.GetName() - default: - panic("virtual service, role, fileor schema must be set") - } -} - -func (item *StorableItem) GetResourceVersion() string { - switch { - case item.Schema != nil: - if item.Schema.GetMetadata() == nil { - return "" - } - return item.Schema.GetMetadata().GetResourceVersion() - case item.ResolverMap != nil: - if item.ResolverMap.GetMetadata() == nil { - return "" - } - return item.ResolverMap.GetMetadata().GetResourceVersion() - default: - panic("virtual service, role, fileor schema must be set") - } -} - -func (item *StorableItem) SetResourceVersion(rv string) { - switch { - case item.Schema != nil: - if item.Schema.GetMetadata() == nil { - item.Schema.Metadata = &gloov1.Metadata{} - } - item.Schema.Metadata.ResourceVersion = rv - case item.ResolverMap != nil: - if item.ResolverMap.GetMetadata() == nil { - item.ResolverMap.Metadata = &gloov1.Metadata{} - } - item.ResolverMap.Metadata.ResourceVersion = rv - default: - panic("virtual service, role, fileor schema must be set") - } -} - -func (item *StorableItem) GetBytes() ([]byte, error) { - switch { - case item.Schema != nil: - return proto.Marshal(item.Schema) - case item.ResolverMap != nil: - return proto.Marshal(item.ResolverMap) - default: - panic("virtual service, role, fileor schema must be set") - } -} - -func (item *StorableItem) GetTypeFlag() StorableItemType { - switch { - case item.Schema != nil: - return StorableItemTypeSchema - case item.ResolverMap != nil: - return StorableItemTypeResolverMap - default: - panic("virtual service, role, fileor schema must be set") - } -} - -type StorableItemType uint64 - -const ( - StorableItemTypeSchema StorableItemType = iota - StorableItemTypeResolverMap -) - -type StorableItemEventHandler struct { - SchemaEventHandler storage.SchemaEventHandler - ResolverMapEventHandler storage.ResolverMapEventHandler -} diff --git a/pkg/storage/consul/client_template.go.tmpl b/pkg/storage/consul/client_template.go.tmpl deleted file mode 100644 index b79b5ec..0000000 --- a/pkg/storage/consul/client_template.go.tmpl +++ /dev/null @@ -1,67 +0,0 @@ -package consul - -import ( - "github.com/pkg/errors" - - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/storage" - "github.com/solo-io/sqoop/pkg/storage/base" -) - -type {{ .LowercasePluralName }}Client struct { - base *base.ConsulStorageClient -} - -func (c *{{ .LowercasePluralName }}Client) Create(item *v1.{{ .UppercaseName }}) (*v1.{{ .UppercaseName }}, error) { - if item.Name == "" { - return nil, errors.Errorf("name required") - } - out, err := c.base.Create(&base.StorableItem{{"{"}}{{ .UppercaseName }}: item}) - if err != nil { - return nil, err - } - return out.{{ .UppercaseName }}, nil -} - -func (c *{{ .LowercasePluralName }}Client) Update(item *v1.{{ .UppercaseName }}) (*v1.{{ .UppercaseName }}, error) { - if item.Name == "" { - return nil, errors.Errorf("name required") - } - out, err := c.base.Update(&base.StorableItem{{"{"}}{{ .UppercaseName }}: item}) - if err != nil { - return nil, err - } - return out.{{ .UppercaseName }}, nil -} - -func (c *{{ .LowercasePluralName }}Client) Delete(name string) error { - return c.base.Delete(name) -} - -func (c *{{ .LowercasePluralName }}Client) Get(name string) (*v1.{{ .UppercaseName }}, error) { - out, err := c.base.Get(name) - if err != nil { - return nil, err - } - return out.{{ .UppercaseName }}, nil -} - -func (c *{{ .LowercasePluralName }}Client) List() ([]*v1.{{ .UppercaseName }}, error) { - list, err := c.base.List() - if err != nil { - return nil, err - } - var {{ .LowercasePluralName }} []*v1.{{ .UppercaseName }} - for _, obj := range list { - {{ .LowercasePluralName }} = append({{ .LowercasePluralName }}, obj.{{ .UppercaseName }}) - } - return {{ .LowercasePluralName }}, nil -} - -func (c *{{ .LowercasePluralName }}Client) Watch(handlers ...storage.{{ .UppercaseName }}EventHandler) (*storage.Watcher, error) { - var baseHandlers []base.StorableItemEventHandler - for _, h := range handlers { - baseHandlers = append(baseHandlers, base.StorableItemEventHandler{{"{"}}{{ .UppercaseName }}EventHandler: h}) - } - return c.base.Watch(baseHandlers...) -} diff --git a/pkg/storage/consul/consul_storage_client.go b/pkg/storage/consul/consul_storage_client.go deleted file mode 100644 index ed9ac07..0000000 --- a/pkg/storage/consul/consul_storage_client.go +++ /dev/null @@ -1,58 +0,0 @@ -package consul - -import ( - "time" - - "github.com/hashicorp/consul/api" - "github.com/pkg/errors" - "github.com/solo-io/sqoop/pkg/storage" - "github.com/solo-io/sqoop/pkg/storage/base" -) - -//go:generate go run ${GOPATH}/src/github.com/solo-io/sqoop/pkg/storage/generate/generate_clients.go -f ${GOPATH}/src/github.com/solo-io/sqoop/pkg/storage/consul/client_template.go.tmpl -o ${GOPATH}/src/github.com/solo-io/sqoop/pkg/storage/consul/ -type Client struct { - v1 *v1client -} - -// TODO: support basic auth and tls -func NewStorage(cfg *api.Config, rootPath string, syncFrequency time.Duration) (storage.Interface, error) { - cfg.WaitTime = syncFrequency - - // Get a new client - client, err := api.NewClient(cfg) - if err != nil { - return nil, errors.Wrap(err, "creating consul client") - } - - return &Client{ - v1: &v1client{ - schemas: &schemasClient{ - base: base.NewConsulStorageClient(rootPath+"/schemas", client), - }, - resolverMaps: &resolverMapsClient{ - base: base.NewConsulStorageClient(rootPath+"/resolverMaps", client), - }, - }, - }, nil -} - -func (c *Client) V1() storage.V1 { - return c.v1 -} - -type v1client struct { - schemas *schemasClient - resolverMaps *resolverMapsClient -} - -func (c *v1client) Register() error { - return nil -} - -func (c *v1client) Schemas() storage.Schemas { - return c.schemas -} - -func (c *v1client) ResolverMaps() storage.ResolverMaps { - return c.resolverMaps -} diff --git a/pkg/storage/consul/consul_storage_client_test.go b/pkg/storage/consul/consul_storage_client_test.go deleted file mode 100644 index 2046252..0000000 --- a/pkg/storage/consul/consul_storage_client_test.go +++ /dev/null @@ -1,377 +0,0 @@ -package consul_test - -import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - "time" - - "fmt" - - "github.com/gogo/protobuf/proto" - "github.com/hashicorp/consul/api" - "github.com/solo-io/gloo/test/helpers" - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/storage" - . "github.com/solo-io/sqoop/pkg/storage/consul" -) - -var _ = Describe("ConsulStorageClient", func() { - var rootPath string - var consul *api.Client - BeforeEach(func() { - rootPath = helpers.RandString(4) - c, err := api.NewClient(api.DefaultConfig()) - Expect(err).NotTo(HaveOccurred()) - consul = c - }) - AfterEach(func() { - consul.KV().DeleteTree(rootPath, nil) - }) - Describe("Schemas", func() { - Describe("create", func() { - It("creates the schema as a consul key", func() { - client, err := NewStorage(api.DefaultConfig(), rootPath, time.Second) - Expect(err).NotTo(HaveOccurred()) - input := &v1.Schema{ - Name: "myschema", - InlineSchema: "foo", - ResolverMap: "myresolvers", - } - schema, err := client.V1().Schemas().Create(input) - Expect(err).NotTo(HaveOccurred()) - Expect(schema).NotTo(Equal(input)) - p, _, err := consul.KV().Get(rootPath+"/schemas/"+input.Name, nil) - Expect(err).NotTo(HaveOccurred()) - var unmarshalledSchema v1.Schema - err = proto.Unmarshal(p.Value, &unmarshalledSchema) - Expect(err).NotTo(HaveOccurred()) - Expect(&unmarshalledSchema).To(Equal(input)) - resourceVersion := fmt.Sprintf("%v", p.CreateIndex) - Expect(schema.Metadata.ResourceVersion).To(Equal(resourceVersion)) - input.Metadata = schema.Metadata - Expect(schema).To(Equal(input)) - }) - It("errors when creating the same schema twice", func() { - client, err := NewStorage(api.DefaultConfig(), rootPath, time.Second) - Expect(err).NotTo(HaveOccurred()) - input := &v1.Schema{ - Name: "myschema", - InlineSchema: "foo", - ResolverMap: "myresolvers", - } - _, err = client.V1().Schemas().Create(input) - Expect(err).NotTo(HaveOccurred()) - _, err = client.V1().Schemas().Create(input) - Expect(err).To(HaveOccurred()) - }) - Describe("update", func() { - It("fails if the schema doesn't exist", func() { - client, err := NewStorage(api.DefaultConfig(), rootPath, time.Second) - Expect(err).NotTo(HaveOccurred()) - input := &v1.Schema{ - Name: "myschema", - InlineSchema: "foo", - ResolverMap: "myresolvers", - } - schema, err := client.V1().Schemas().Update(input) - Expect(err).To(HaveOccurred()) - Expect(schema).To(BeNil()) - }) - It("fails if the resourceversion is not up to date", func() { - client, err := NewStorage(api.DefaultConfig(), rootPath, time.Second) - Expect(err).NotTo(HaveOccurred()) - input := &v1.Schema{ - Name: "myschema", - InlineSchema: "foo", - ResolverMap: "myresolvers", - } - _, err = client.V1().Schemas().Create(input) - Expect(err).NotTo(HaveOccurred()) - v, err := client.V1().Schemas().Update(input) - Expect(v).To(BeNil()) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("resource version")) - }) - It("updates the schema", func() { - client, err := NewStorage(api.DefaultConfig(), rootPath, time.Second) - Expect(err).NotTo(HaveOccurred()) - input := &v1.Schema{ - Name: "myschema", - InlineSchema: "foo", - ResolverMap: "myresolvers", - } - schema, err := client.V1().Schemas().Create(input) - Expect(err).NotTo(HaveOccurred()) - changed := proto.Clone(input).(*v1.Schema) - changed.InlineSchema = "bar" - // match resource version - changed.Metadata = schema.Metadata - out, err := client.V1().Schemas().Update(changed) - Expect(err).NotTo(HaveOccurred()) - Expect(out.InlineSchema).To(Equal(changed.InlineSchema)) - }) - Describe("get", func() { - It("fails if the schema doesn't exist", func() { - client, err := NewStorage(api.DefaultConfig(), rootPath, time.Second) - Expect(err).NotTo(HaveOccurred()) - schema, err := client.V1().Schemas().Get("foo") - Expect(err).To(HaveOccurred()) - Expect(schema).To(BeNil()) - }) - It("returns the schema", func() { - client, err := NewStorage(api.DefaultConfig(), rootPath, time.Second) - Expect(err).NotTo(HaveOccurred()) - input := &v1.Schema{ - Name: "myschema", - InlineSchema: "foo", - ResolverMap: "myresolvers", - } - schema, err := client.V1().Schemas().Create(input) - Expect(err).NotTo(HaveOccurred()) - out, err := client.V1().Schemas().Get(input.Name) - Expect(err).NotTo(HaveOccurred()) - Expect(out).To(Equal(schema)) - input.Metadata = out.Metadata - Expect(out).To(Equal(input)) - }) - }) - Describe("list", func() { - It("returns all existing schemas", func() { - client, err := NewStorage(api.DefaultConfig(), rootPath, time.Second) - Expect(err).NotTo(HaveOccurred()) - input1 := &v1.Schema{ - Name: "myschema1", - } - input2 := &v1.Schema{ - Name: "myschema2", - } - input3 := &v1.Schema{ - Name: "myschema3", - } - schema1, err := client.V1().Schemas().Create(input1) - Expect(err).NotTo(HaveOccurred()) - schema2, err := client.V1().Schemas().Create(input2) - Expect(err).NotTo(HaveOccurred()) - schema3, err := client.V1().Schemas().Create(input3) - Expect(err).NotTo(HaveOccurred()) - out, err := client.V1().Schemas().List() - Expect(err).NotTo(HaveOccurred()) - Expect(out).To(ContainElement(schema1)) - Expect(out).To(ContainElement(schema2)) - Expect(out).To(ContainElement(schema3)) - }) - }) - Describe("watch", func() { - It("watches", func() { - client, err := NewStorage(api.DefaultConfig(), rootPath, time.Second) - Expect(err).NotTo(HaveOccurred()) - lists := make(chan []*v1.Schema, 3) - stop := make(chan struct{}) - defer close(stop) - errs := make(chan error) - w, err := client.V1().Schemas().Watch(&storage.SchemaEventHandlerFuncs{ - UpdateFunc: func(updatedList []*v1.Schema, _ *v1.Schema) { - lists <- updatedList - }, - }) - Expect(err).NotTo(HaveOccurred()) - go func() { - w.Run(stop, errs) - }() - input1 := &v1.Schema{ - Name: "myschema1", - } - input2 := &v1.Schema{ - Name: "myschema2", - } - input3 := &v1.Schema{ - Name: "myschema3", - } - schema1, err := client.V1().Schemas().Create(input1) - Expect(err).NotTo(HaveOccurred()) - schema2, err := client.V1().Schemas().Create(input2) - Expect(err).NotTo(HaveOccurred()) - schema3, err := client.V1().Schemas().Create(input3) - Expect(err).NotTo(HaveOccurred()) - - var list []*v1.Schema - Eventually(func() []*v1.Schema { - select { - default: - return nil - case l := <-lists: - list = l - return l - } - }).Should(HaveLen(3)) - Expect(list).To(HaveLen(3)) - Expect(list).To(ContainElement(schema1)) - Expect(list).To(ContainElement(schema2)) - Expect(list).To(ContainElement(schema3)) - }) - }) - }) - }) - }) - Describe("ResolverMaps", func() { - Describe("create", func() { - It("creates the resolverMap as a consul key", func() { - client, err := NewStorage(api.DefaultConfig(), rootPath, time.Second) - Expect(err).NotTo(HaveOccurred()) - input := &v1.ResolverMap{ - Name: "myresolverMap", - } - resolverMap, err := client.V1().ResolverMaps().Create(input) - Expect(err).NotTo(HaveOccurred()) - Expect(resolverMap).NotTo(Equal(input)) - p, _, err := consul.KV().Get(rootPath+"/resolverMaps/"+input.Name, nil) - Expect(err).NotTo(HaveOccurred()) - var unmarshalledResolverMap v1.ResolverMap - err = proto.Unmarshal(p.Value, &unmarshalledResolverMap) - Expect(err).NotTo(HaveOccurred()) - Expect(&unmarshalledResolverMap).To(Equal(input)) - resourceVersion := fmt.Sprintf("%v", p.CreateIndex) - Expect(resolverMap.Metadata.ResourceVersion).To(Equal(resourceVersion)) - input.Metadata = resolverMap.Metadata - Expect(resolverMap).To(Equal(input)) - }) - It("errors when creating the same resolverMap twice", func() { - client, err := NewStorage(api.DefaultConfig(), rootPath, time.Second) - Expect(err).NotTo(HaveOccurred()) - input := &v1.ResolverMap{ - Name: "myresolverMap", - } - _, err = client.V1().ResolverMaps().Create(input) - Expect(err).NotTo(HaveOccurred()) - _, err = client.V1().ResolverMaps().Create(input) - Expect(err).To(HaveOccurred()) - }) - Describe("update", func() { - It("fails if the resolverMap doesn't exist", func() { - client, err := NewStorage(api.DefaultConfig(), rootPath, time.Second) - Expect(err).NotTo(HaveOccurred()) - input := &v1.ResolverMap{ - Name: "myresolverMap", - } - resolverMap, err := client.V1().ResolverMaps().Update(input) - Expect(err).To(HaveOccurred()) - Expect(resolverMap).To(BeNil()) - }) - It("fails if the resourceversion is not up to date", func() { - client, err := NewStorage(api.DefaultConfig(), rootPath, time.Second) - Expect(err).NotTo(HaveOccurred()) - input := &v1.ResolverMap{ - Name: "myresolverMap", - } - _, err = client.V1().ResolverMaps().Create(input) - Expect(err).NotTo(HaveOccurred()) - v, err := client.V1().ResolverMaps().Update(input) - Expect(v).To(BeNil()) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("resource version")) - }) - Describe("get", func() { - It("fails if the resolverMap doesn't exist", func() { - client, err := NewStorage(api.DefaultConfig(), rootPath, time.Second) - Expect(err).NotTo(HaveOccurred()) - resolverMap, err := client.V1().ResolverMaps().Get("foo") - Expect(err).To(HaveOccurred()) - Expect(resolverMap).To(BeNil()) - }) - It("returns the resolverMap", func() { - client, err := NewStorage(api.DefaultConfig(), rootPath, time.Second) - Expect(err).NotTo(HaveOccurred()) - input := &v1.ResolverMap{ - Name: "myresolverMap", - } - resolverMap, err := client.V1().ResolverMaps().Create(input) - Expect(err).NotTo(HaveOccurred()) - out, err := client.V1().ResolverMaps().Get(input.Name) - Expect(err).NotTo(HaveOccurred()) - Expect(out).To(Equal(resolverMap)) - input.Metadata = out.Metadata - Expect(out).To(Equal(input)) - }) - }) - Describe("list", func() { - It("returns all existing resolverMaps", func() { - client, err := NewStorage(api.DefaultConfig(), rootPath, time.Second) - Expect(err).NotTo(HaveOccurred()) - input1 := &v1.ResolverMap{ - Name: "myresolverMap1", - } - input2 := &v1.ResolverMap{ - Name: "myresolverMap2", - } - input3 := &v1.ResolverMap{ - Name: "myresolverMap3", - } - resolverMap1, err := client.V1().ResolverMaps().Create(input1) - Expect(err).NotTo(HaveOccurred()) - time.Sleep(time.Second) - resolverMap2, err := client.V1().ResolverMaps().Create(input2) - Expect(err).NotTo(HaveOccurred()) - time.Sleep(time.Second) - resolverMap3, err := client.V1().ResolverMaps().Create(input3) - Expect(err).NotTo(HaveOccurred()) - out, err := client.V1().ResolverMaps().List() - Expect(err).NotTo(HaveOccurred()) - Expect(out).To(ContainElement(resolverMap1)) - Expect(out).To(ContainElement(resolverMap2)) - Expect(out).To(ContainElement(resolverMap3)) - }) - }) - Describe("watch", func() { - It("watches", func() { - client, err := NewStorage(api.DefaultConfig(), rootPath, time.Second) - Expect(err).NotTo(HaveOccurred()) - lists := make(chan []*v1.ResolverMap, 3) - stop := make(chan struct{}) - defer close(stop) - errs := make(chan error) - w, err := client.V1().ResolverMaps().Watch(&storage.ResolverMapEventHandlerFuncs{ - UpdateFunc: func(updatedList []*v1.ResolverMap, _ *v1.ResolverMap) { - lists <- updatedList - }, - }) - Expect(err).NotTo(HaveOccurred()) - go func() { - w.Run(stop, errs) - }() - input1 := &v1.ResolverMap{ - Name: "myresolverMap1", - } - input2 := &v1.ResolverMap{ - Name: "myresolverMap2", - } - input3 := &v1.ResolverMap{ - Name: "myresolverMap3", - } - resolverMap1, err := client.V1().ResolverMaps().Create(input1) - Expect(err).NotTo(HaveOccurred()) - resolverMap2, err := client.V1().ResolverMaps().Create(input2) - Expect(err).NotTo(HaveOccurred()) - resolverMap3, err := client.V1().ResolverMaps().Create(input3) - Expect(err).NotTo(HaveOccurred()) - - var list []*v1.ResolverMap - Eventually(func() []*v1.ResolverMap { - select { - default: - return nil - case l := <-lists: - list = l - return l - } - }).Should(HaveLen(3)) - Expect(list).To(HaveLen(3)) - Expect(list).To(ContainElement(resolverMap1)) - Expect(list).To(ContainElement(resolverMap2)) - Expect(list).To(ContainElement(resolverMap3)) - }) - }) - }) - }) - }) -}) diff --git a/pkg/storage/consul/consul_suite_test.go b/pkg/storage/consul/consul_suite_test.go deleted file mode 100644 index f20c527..0000000 --- a/pkg/storage/consul/consul_suite_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package consul - -import ( - "os" - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "github.com/solo-io/gloo/pkg/log" - "github.com/solo-io/gloo/test/helpers" - "github.com/solo-io/gloo/test/helpers/local" -) - -func TestConsul(t *testing.T) { - if os.Getenv("RUN_CONSUL_TESTS") != "1" { - log.Printf("This test downloads and runs consul and is disabled by default. To enable, set RUN_CONSUL_TESTS=1 in your env.") - return - } - RegisterFailHandler(Fail) - log.DefaultOut = GinkgoWriter - RunSpecs(t, "Consul Suite") -} - -var ( - consulFactory *localhelpers.ConsulFactory - consulInstance *localhelpers.ConsulInstance - err error -) - -var _ = BeforeSuite(func() { - consulFactory, err = localhelpers.NewConsulFactory() - helpers.Must(err) - consulInstance, err = consulFactory.NewConsulInstance() - helpers.Must(err) - err = consulInstance.Run() - helpers.Must(err) -}) - -var _ = AfterSuite(func() { - consulInstance.Clean() - consulFactory.Clean() -}) diff --git a/pkg/storage/consul/resolver_maps.go b/pkg/storage/consul/resolver_maps.go deleted file mode 100644 index eb06f51..0000000 --- a/pkg/storage/consul/resolver_maps.go +++ /dev/null @@ -1,67 +0,0 @@ -package consul - -import ( - "github.com/pkg/errors" - - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/storage" - "github.com/solo-io/sqoop/pkg/storage/base" -) - -type resolverMapsClient struct { - base *base.ConsulStorageClient -} - -func (c *resolverMapsClient) Create(item *v1.ResolverMap) (*v1.ResolverMap, error) { - if item.Name == "" { - return nil, errors.Errorf("name required") - } - out, err := c.base.Create(&base.StorableItem{ResolverMap: item}) - if err != nil { - return nil, err - } - return out.ResolverMap, nil -} - -func (c *resolverMapsClient) Update(item *v1.ResolverMap) (*v1.ResolverMap, error) { - if item.Name == "" { - return nil, errors.Errorf("name required") - } - out, err := c.base.Update(&base.StorableItem{ResolverMap: item}) - if err != nil { - return nil, err - } - return out.ResolverMap, nil -} - -func (c *resolverMapsClient) Delete(name string) error { - return c.base.Delete(name) -} - -func (c *resolverMapsClient) Get(name string) (*v1.ResolverMap, error) { - out, err := c.base.Get(name) - if err != nil { - return nil, err - } - return out.ResolverMap, nil -} - -func (c *resolverMapsClient) List() ([]*v1.ResolverMap, error) { - list, err := c.base.List() - if err != nil { - return nil, err - } - var resolverMaps []*v1.ResolverMap - for _, obj := range list { - resolverMaps = append(resolverMaps, obj.ResolverMap) - } - return resolverMaps, nil -} - -func (c *resolverMapsClient) Watch(handlers ...storage.ResolverMapEventHandler) (*storage.Watcher, error) { - var baseHandlers []base.StorableItemEventHandler - for _, h := range handlers { - baseHandlers = append(baseHandlers, base.StorableItemEventHandler{ResolverMapEventHandler: h}) - } - return c.base.Watch(baseHandlers...) -} diff --git a/pkg/storage/consul/schemas.go b/pkg/storage/consul/schemas.go deleted file mode 100644 index f4f56de..0000000 --- a/pkg/storage/consul/schemas.go +++ /dev/null @@ -1,67 +0,0 @@ -package consul - -import ( - "github.com/pkg/errors" - - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/storage" - "github.com/solo-io/sqoop/pkg/storage/base" -) - -type schemasClient struct { - base *base.ConsulStorageClient -} - -func (c *schemasClient) Create(item *v1.Schema) (*v1.Schema, error) { - if item.Name == "" { - return nil, errors.Errorf("name required") - } - out, err := c.base.Create(&base.StorableItem{Schema: item}) - if err != nil { - return nil, err - } - return out.Schema, nil -} - -func (c *schemasClient) Update(item *v1.Schema) (*v1.Schema, error) { - if item.Name == "" { - return nil, errors.Errorf("name required") - } - out, err := c.base.Update(&base.StorableItem{Schema: item}) - if err != nil { - return nil, err - } - return out.Schema, nil -} - -func (c *schemasClient) Delete(name string) error { - return c.base.Delete(name) -} - -func (c *schemasClient) Get(name string) (*v1.Schema, error) { - out, err := c.base.Get(name) - if err != nil { - return nil, err - } - return out.Schema, nil -} - -func (c *schemasClient) List() ([]*v1.Schema, error) { - list, err := c.base.List() - if err != nil { - return nil, err - } - var schemas []*v1.Schema - for _, obj := range list { - schemas = append(schemas, obj.Schema) - } - return schemas, nil -} - -func (c *schemasClient) Watch(handlers ...storage.SchemaEventHandler) (*storage.Watcher, error) { - var baseHandlers []base.StorableItemEventHandler - for _, h := range handlers { - baseHandlers = append(baseHandlers, base.StorableItemEventHandler{SchemaEventHandler: h}) - } - return c.base.Watch(baseHandlers...) -} diff --git a/pkg/storage/crd/client/clientset/versioned/clientset.go b/pkg/storage/crd/client/clientset/versioned/clientset.go deleted file mode 100644 index ebb4904..0000000 --- a/pkg/storage/crd/client/clientset/versioned/clientset.go +++ /dev/null @@ -1,98 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package versioned - -import ( - sqoopv1 "github.com/solo-io/sqoop/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1" - discovery "k8s.io/client-go/discovery" - rest "k8s.io/client-go/rest" - flowcontrol "k8s.io/client-go/util/flowcontrol" -) - -type Interface interface { - Discovery() discovery.DiscoveryInterface - SqoopV1() sqoopv1.SqoopV1Interface - // Deprecated: please explicitly pick a version if possible. - Sqoop() sqoopv1.SqoopV1Interface -} - -// Clientset contains the clients for groups. Each group has exactly one -// version included in a Clientset. -type Clientset struct { - *discovery.DiscoveryClient - sqoopV1 *sqoopv1.SqoopV1Client -} - -// SqoopV1 retrieves the SqoopV1Client -func (c *Clientset) SqoopV1() sqoopv1.SqoopV1Interface { - return c.sqoopV1 -} - -// Deprecated: Sqoop retrieves the default version of SqoopClient. -// Please explicitly pick a version. -func (c *Clientset) Sqoop() sqoopv1.SqoopV1Interface { - return c.sqoopV1 -} - -// Discovery retrieves the DiscoveryClient -func (c *Clientset) Discovery() discovery.DiscoveryInterface { - if c == nil { - return nil - } - return c.DiscoveryClient -} - -// NewForConfig creates a new Clientset for the given config. -func NewForConfig(c *rest.Config) (*Clientset, error) { - configShallowCopy := *c - if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { - configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) - } - var cs Clientset - var err error - cs.sqoopV1, err = sqoopv1.NewForConfig(&configShallowCopy) - if err != nil { - return nil, err - } - - cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) - if err != nil { - return nil, err - } - return &cs, nil -} - -// NewForConfigOrDie creates a new Clientset for the given config and -// panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *Clientset { - var cs Clientset - cs.sqoopV1 = sqoopv1.NewForConfigOrDie(c) - - cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) - return &cs -} - -// New creates a new Clientset for the given RESTClient. -func New(c rest.Interface) *Clientset { - var cs Clientset - cs.sqoopV1 = sqoopv1.New(c) - - cs.DiscoveryClient = discovery.NewDiscoveryClient(c) - return &cs -} diff --git a/pkg/storage/crd/client/clientset/versioned/doc.go b/pkg/storage/crd/client/clientset/versioned/doc.go deleted file mode 100644 index 41721ca..0000000 --- a/pkg/storage/crd/client/clientset/versioned/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated clientset. -package versioned diff --git a/pkg/storage/crd/client/clientset/versioned/fake/clientset_generated.go b/pkg/storage/crd/client/clientset/versioned/fake/clientset_generated.go deleted file mode 100644 index 9479fb8..0000000 --- a/pkg/storage/crd/client/clientset/versioned/fake/clientset_generated.go +++ /dev/null @@ -1,82 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - clientset "github.com/solo-io/sqoop/pkg/storage/crd/client/clientset/versioned" - sqoopv1 "github.com/solo-io/sqoop/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1" - fakesqoopv1 "github.com/solo-io/sqoop/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/fake" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/discovery" - fakediscovery "k8s.io/client-go/discovery/fake" - "k8s.io/client-go/testing" -) - -// NewSimpleClientset returns a clientset that will respond with the provided objects. -// It's backed by a very simple object tracker that processes creates, updates and deletions as-is, -// without applying any validations and/or defaults. It shouldn't be considered a replacement -// for a real clientset and is mostly useful in simple unit tests. -func NewSimpleClientset(objects ...runtime.Object) *Clientset { - o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) - for _, obj := range objects { - if err := o.Add(obj); err != nil { - panic(err) - } - } - - cs := &Clientset{} - cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} - cs.AddReactor("*", "*", testing.ObjectReaction(o)) - cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { - gvr := action.GetResource() - ns := action.GetNamespace() - watch, err := o.Watch(gvr, ns) - if err != nil { - return false, nil, err - } - return true, watch, nil - }) - - return cs -} - -// Clientset implements clientset.Interface. Meant to be embedded into a -// struct to get a default implementation. This makes faking out just the method -// you want to test easier. -type Clientset struct { - testing.Fake - discovery *fakediscovery.FakeDiscovery -} - -func (c *Clientset) Discovery() discovery.DiscoveryInterface { - return c.discovery -} - -var _ clientset.Interface = &Clientset{} - -// SqoopV1 retrieves the SqoopV1Client -func (c *Clientset) SqoopV1() sqoopv1.SqoopV1Interface { - return &fakesqoopv1.FakeSqoopV1{Fake: &c.Fake} -} - -// Sqoop retrieves the SqoopV1Client -func (c *Clientset) Sqoop() sqoopv1.SqoopV1Interface { - return &fakesqoopv1.FakeSqoopV1{Fake: &c.Fake} -} diff --git a/pkg/storage/crd/client/clientset/versioned/fake/doc.go b/pkg/storage/crd/client/clientset/versioned/fake/doc.go deleted file mode 100644 index 9b99e71..0000000 --- a/pkg/storage/crd/client/clientset/versioned/fake/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated fake clientset. -package fake diff --git a/pkg/storage/crd/client/clientset/versioned/fake/register.go b/pkg/storage/crd/client/clientset/versioned/fake/register.go deleted file mode 100644 index d065832..0000000 --- a/pkg/storage/crd/client/clientset/versioned/fake/register.go +++ /dev/null @@ -1,54 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - sqoopv1 "github.com/solo-io/sqoop/pkg/storage/crd/solo.io/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - schema "k8s.io/apimachinery/pkg/runtime/schema" - serializer "k8s.io/apimachinery/pkg/runtime/serializer" -) - -var scheme = runtime.NewScheme() -var codecs = serializer.NewCodecFactory(scheme) -var parameterCodec = runtime.NewParameterCodec(scheme) - -func init() { - v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) - AddToScheme(scheme) -} - -// AddToScheme adds all types of this clientset into the given scheme. This allows composition -// of clientsets, like in: -// -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) -// -// kclientset, _ := kubernetes.NewForConfig(c) -// aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) -// -// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types -// correctly. -func AddToScheme(scheme *runtime.Scheme) { - sqoopv1.AddToScheme(scheme) -} diff --git a/pkg/storage/crd/client/clientset/versioned/scheme/doc.go b/pkg/storage/crd/client/clientset/versioned/scheme/doc.go deleted file mode 100644 index 7dc3756..0000000 --- a/pkg/storage/crd/client/clientset/versioned/scheme/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package contains the scheme of the automatically generated clientset. -package scheme diff --git a/pkg/storage/crd/client/clientset/versioned/scheme/register.go b/pkg/storage/crd/client/clientset/versioned/scheme/register.go deleted file mode 100644 index 4d5edcd..0000000 --- a/pkg/storage/crd/client/clientset/versioned/scheme/register.go +++ /dev/null @@ -1,54 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package scheme - -import ( - sqoopv1 "github.com/solo-io/sqoop/pkg/storage/crd/solo.io/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - schema "k8s.io/apimachinery/pkg/runtime/schema" - serializer "k8s.io/apimachinery/pkg/runtime/serializer" -) - -var Scheme = runtime.NewScheme() -var Codecs = serializer.NewCodecFactory(Scheme) -var ParameterCodec = runtime.NewParameterCodec(Scheme) - -func init() { - v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) - AddToScheme(Scheme) -} - -// AddToScheme adds all types of this clientset into the given scheme. This allows composition -// of clientsets, like in: -// -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) -// -// kclientset, _ := kubernetes.NewForConfig(c) -// aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) -// -// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types -// correctly. -func AddToScheme(scheme *runtime.Scheme) { - sqoopv1.AddToScheme(scheme) -} diff --git a/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/doc.go b/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/doc.go deleted file mode 100644 index 3af5d05..0000000 --- a/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated typed clients. -package v1 diff --git a/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/fake/doc.go b/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/fake/doc.go deleted file mode 100644 index 16f4439..0000000 --- a/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/fake/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// Package fake has the automatically generated clients. -package fake diff --git a/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/fake/fake_resolvermap.go b/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/fake/fake_resolvermap.go deleted file mode 100644 index a4e9778..0000000 --- a/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/fake/fake_resolvermap.go +++ /dev/null @@ -1,128 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - solo_io_v1 "github.com/solo-io/sqoop/pkg/storage/crd/solo.io/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeResolverMaps implements ResolverMapInterface -type FakeResolverMaps struct { - Fake *FakeSqoopV1 - ns string -} - -var resolvermapsResource = schema.GroupVersionResource{Group: "sqoop.solo.io", Version: "v1", Resource: "resolvermaps"} - -var resolvermapsKind = schema.GroupVersionKind{Group: "sqoop.solo.io", Version: "v1", Kind: "ResolverMap"} - -// Get takes name of the resolverMap, and returns the corresponding resolverMap object, and an error if there is any. -func (c *FakeResolverMaps) Get(name string, options v1.GetOptions) (result *solo_io_v1.ResolverMap, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(resolvermapsResource, c.ns, name), &solo_io_v1.ResolverMap{}) - - if obj == nil { - return nil, err - } - return obj.(*solo_io_v1.ResolverMap), err -} - -// List takes label and field selectors, and returns the list of ResolverMaps that match those selectors. -func (c *FakeResolverMaps) List(opts v1.ListOptions) (result *solo_io_v1.ResolverMapList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(resolvermapsResource, resolvermapsKind, c.ns, opts), &solo_io_v1.ResolverMapList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &solo_io_v1.ResolverMapList{} - for _, item := range obj.(*solo_io_v1.ResolverMapList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested resolverMaps. -func (c *FakeResolverMaps) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(resolvermapsResource, c.ns, opts)) - -} - -// Create takes the representation of a resolverMap and creates it. Returns the server's representation of the resolverMap, and an error, if there is any. -func (c *FakeResolverMaps) Create(resolverMap *solo_io_v1.ResolverMap) (result *solo_io_v1.ResolverMap, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(resolvermapsResource, c.ns, resolverMap), &solo_io_v1.ResolverMap{}) - - if obj == nil { - return nil, err - } - return obj.(*solo_io_v1.ResolverMap), err -} - -// Update takes the representation of a resolverMap and updates it. Returns the server's representation of the resolverMap, and an error, if there is any. -func (c *FakeResolverMaps) Update(resolverMap *solo_io_v1.ResolverMap) (result *solo_io_v1.ResolverMap, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(resolvermapsResource, c.ns, resolverMap), &solo_io_v1.ResolverMap{}) - - if obj == nil { - return nil, err - } - return obj.(*solo_io_v1.ResolverMap), err -} - -// Delete takes name of the resolverMap and deletes it. Returns an error if one occurs. -func (c *FakeResolverMaps) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(resolvermapsResource, c.ns, name), &solo_io_v1.ResolverMap{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeResolverMaps) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(resolvermapsResource, c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &solo_io_v1.ResolverMapList{}) - return err -} - -// Patch applies the patch and returns the patched resolverMap. -func (c *FakeResolverMaps) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *solo_io_v1.ResolverMap, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(resolvermapsResource, c.ns, name, data, subresources...), &solo_io_v1.ResolverMap{}) - - if obj == nil { - return nil, err - } - return obj.(*solo_io_v1.ResolverMap), err -} diff --git a/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/fake/fake_schema.go b/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/fake/fake_schema.go deleted file mode 100644 index acb8af1..0000000 --- a/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/fake/fake_schema.go +++ /dev/null @@ -1,128 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - solo_io_v1 "github.com/solo-io/sqoop/pkg/storage/crd/solo.io/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeSchemas implements SchemaInterface -type FakeSchemas struct { - Fake *FakeSqoopV1 - ns string -} - -var schemasResource = schema.GroupVersionResource{Group: "sqoop.solo.io", Version: "v1", Resource: "schemas"} - -var schemasKind = schema.GroupVersionKind{Group: "sqoop.solo.io", Version: "v1", Kind: "Schema"} - -// Get takes name of the schema, and returns the corresponding schema object, and an error if there is any. -func (c *FakeSchemas) Get(name string, options v1.GetOptions) (result *solo_io_v1.Schema, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(schemasResource, c.ns, name), &solo_io_v1.Schema{}) - - if obj == nil { - return nil, err - } - return obj.(*solo_io_v1.Schema), err -} - -// List takes label and field selectors, and returns the list of Schemas that match those selectors. -func (c *FakeSchemas) List(opts v1.ListOptions) (result *solo_io_v1.SchemaList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(schemasResource, schemasKind, c.ns, opts), &solo_io_v1.SchemaList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &solo_io_v1.SchemaList{} - for _, item := range obj.(*solo_io_v1.SchemaList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested schemas. -func (c *FakeSchemas) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(schemasResource, c.ns, opts)) - -} - -// Create takes the representation of a schema and creates it. Returns the server's representation of the schema, and an error, if there is any. -func (c *FakeSchemas) Create(schema *solo_io_v1.Schema) (result *solo_io_v1.Schema, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(schemasResource, c.ns, schema), &solo_io_v1.Schema{}) - - if obj == nil { - return nil, err - } - return obj.(*solo_io_v1.Schema), err -} - -// Update takes the representation of a schema and updates it. Returns the server's representation of the schema, and an error, if there is any. -func (c *FakeSchemas) Update(schema *solo_io_v1.Schema) (result *solo_io_v1.Schema, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(schemasResource, c.ns, schema), &solo_io_v1.Schema{}) - - if obj == nil { - return nil, err - } - return obj.(*solo_io_v1.Schema), err -} - -// Delete takes name of the schema and deletes it. Returns an error if one occurs. -func (c *FakeSchemas) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(schemasResource, c.ns, name), &solo_io_v1.Schema{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeSchemas) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(schemasResource, c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &solo_io_v1.SchemaList{}) - return err -} - -// Patch applies the patch and returns the patched schema. -func (c *FakeSchemas) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *solo_io_v1.Schema, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(schemasResource, c.ns, name, data, subresources...), &solo_io_v1.Schema{}) - - if obj == nil { - return nil, err - } - return obj.(*solo_io_v1.Schema), err -} diff --git a/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/fake/fake_solo.io_client.go b/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/fake/fake_solo.io_client.go deleted file mode 100644 index 2757adf..0000000 --- a/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/fake/fake_solo.io_client.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1 "github.com/solo-io/sqoop/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1" - rest "k8s.io/client-go/rest" - testing "k8s.io/client-go/testing" -) - -type FakeSqoopV1 struct { - *testing.Fake -} - -func (c *FakeSqoopV1) ResolverMaps(namespace string) v1.ResolverMapInterface { - return &FakeResolverMaps{c, namespace} -} - -func (c *FakeSqoopV1) Schemas(namespace string) v1.SchemaInterface { - return &FakeSchemas{c, namespace} -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *FakeSqoopV1) RESTClient() rest.Interface { - var ret *rest.RESTClient - return ret -} diff --git a/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/generated_expansion.go b/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/generated_expansion.go deleted file mode 100644 index 65401b3..0000000 --- a/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/generated_expansion.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1 - -type ResolverMapExpansion interface{} - -type SchemaExpansion interface{} diff --git a/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/resolvermap.go b/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/resolvermap.go deleted file mode 100644 index 15677c8..0000000 --- a/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/resolvermap.go +++ /dev/null @@ -1,157 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1 - -import ( - scheme "github.com/solo-io/sqoop/pkg/storage/crd/client/clientset/versioned/scheme" - v1 "github.com/solo-io/sqoop/pkg/storage/crd/solo.io/v1" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// ResolverMapsGetter has a method to return a ResolverMapInterface. -// A group's client should implement this interface. -type ResolverMapsGetter interface { - ResolverMaps(namespace string) ResolverMapInterface -} - -// ResolverMapInterface has methods to work with ResolverMap resources. -type ResolverMapInterface interface { - Create(*v1.ResolverMap) (*v1.ResolverMap, error) - Update(*v1.ResolverMap) (*v1.ResolverMap, error) - Delete(name string, options *meta_v1.DeleteOptions) error - DeleteCollection(options *meta_v1.DeleteOptions, listOptions meta_v1.ListOptions) error - Get(name string, options meta_v1.GetOptions) (*v1.ResolverMap, error) - List(opts meta_v1.ListOptions) (*v1.ResolverMapList, error) - Watch(opts meta_v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.ResolverMap, err error) - ResolverMapExpansion -} - -// resolverMaps implements ResolverMapInterface -type resolverMaps struct { - client rest.Interface - ns string -} - -// newResolverMaps returns a ResolverMaps -func newResolverMaps(c *SqoopV1Client, namespace string) *resolverMaps { - return &resolverMaps{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the resolverMap, and returns the corresponding resolverMap object, and an error if there is any. -func (c *resolverMaps) Get(name string, options meta_v1.GetOptions) (result *v1.ResolverMap, err error) { - result = &v1.ResolverMap{} - err = c.client.Get(). - Namespace(c.ns). - Resource("resolvermaps"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of ResolverMaps that match those selectors. -func (c *resolverMaps) List(opts meta_v1.ListOptions) (result *v1.ResolverMapList, err error) { - result = &v1.ResolverMapList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("resolvermaps"). - VersionedParams(&opts, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested resolverMaps. -func (c *resolverMaps) Watch(opts meta_v1.ListOptions) (watch.Interface, error) { - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("resolvermaps"). - VersionedParams(&opts, scheme.ParameterCodec). - Watch() -} - -// Create takes the representation of a resolverMap and creates it. Returns the server's representation of the resolverMap, and an error, if there is any. -func (c *resolverMaps) Create(resolverMap *v1.ResolverMap) (result *v1.ResolverMap, err error) { - result = &v1.ResolverMap{} - err = c.client.Post(). - Namespace(c.ns). - Resource("resolvermaps"). - Body(resolverMap). - Do(). - Into(result) - return -} - -// Update takes the representation of a resolverMap and updates it. Returns the server's representation of the resolverMap, and an error, if there is any. -func (c *resolverMaps) Update(resolverMap *v1.ResolverMap) (result *v1.ResolverMap, err error) { - result = &v1.ResolverMap{} - err = c.client.Put(). - Namespace(c.ns). - Resource("resolvermaps"). - Name(resolverMap.Name). - Body(resolverMap). - Do(). - Into(result) - return -} - -// Delete takes name of the resolverMap and deletes it. Returns an error if one occurs. -func (c *resolverMaps) Delete(name string, options *meta_v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("resolvermaps"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *resolverMaps) DeleteCollection(options *meta_v1.DeleteOptions, listOptions meta_v1.ListOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("resolvermaps"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched resolverMap. -func (c *resolverMaps) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.ResolverMap, err error) { - result = &v1.ResolverMap{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("resolvermaps"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/schema.go b/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/schema.go deleted file mode 100644 index 8f4e1be..0000000 --- a/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/schema.go +++ /dev/null @@ -1,157 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1 - -import ( - scheme "github.com/solo-io/sqoop/pkg/storage/crd/client/clientset/versioned/scheme" - v1 "github.com/solo-io/sqoop/pkg/storage/crd/solo.io/v1" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// SchemasGetter has a method to return a SchemaInterface. -// A group's client should implement this interface. -type SchemasGetter interface { - Schemas(namespace string) SchemaInterface -} - -// SchemaInterface has methods to work with Schema resources. -type SchemaInterface interface { - Create(*v1.Schema) (*v1.Schema, error) - Update(*v1.Schema) (*v1.Schema, error) - Delete(name string, options *meta_v1.DeleteOptions) error - DeleteCollection(options *meta_v1.DeleteOptions, listOptions meta_v1.ListOptions) error - Get(name string, options meta_v1.GetOptions) (*v1.Schema, error) - List(opts meta_v1.ListOptions) (*v1.SchemaList, error) - Watch(opts meta_v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.Schema, err error) - SchemaExpansion -} - -// schemas implements SchemaInterface -type schemas struct { - client rest.Interface - ns string -} - -// newSchemas returns a Schemas -func newSchemas(c *SqoopV1Client, namespace string) *schemas { - return &schemas{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the schema, and returns the corresponding schema object, and an error if there is any. -func (c *schemas) Get(name string, options meta_v1.GetOptions) (result *v1.Schema, err error) { - result = &v1.Schema{} - err = c.client.Get(). - Namespace(c.ns). - Resource("schemas"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of Schemas that match those selectors. -func (c *schemas) List(opts meta_v1.ListOptions) (result *v1.SchemaList, err error) { - result = &v1.SchemaList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("schemas"). - VersionedParams(&opts, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested schemas. -func (c *schemas) Watch(opts meta_v1.ListOptions) (watch.Interface, error) { - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("schemas"). - VersionedParams(&opts, scheme.ParameterCodec). - Watch() -} - -// Create takes the representation of a schema and creates it. Returns the server's representation of the schema, and an error, if there is any. -func (c *schemas) Create(schema *v1.Schema) (result *v1.Schema, err error) { - result = &v1.Schema{} - err = c.client.Post(). - Namespace(c.ns). - Resource("schemas"). - Body(schema). - Do(). - Into(result) - return -} - -// Update takes the representation of a schema and updates it. Returns the server's representation of the schema, and an error, if there is any. -func (c *schemas) Update(schema *v1.Schema) (result *v1.Schema, err error) { - result = &v1.Schema{} - err = c.client.Put(). - Namespace(c.ns). - Resource("schemas"). - Name(schema.Name). - Body(schema). - Do(). - Into(result) - return -} - -// Delete takes name of the schema and deletes it. Returns an error if one occurs. -func (c *schemas) Delete(name string, options *meta_v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("schemas"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *schemas) DeleteCollection(options *meta_v1.DeleteOptions, listOptions meta_v1.ListOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("schemas"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched schema. -func (c *schemas) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.Schema, err error) { - result = &v1.Schema{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("schemas"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/solo.io_client.go b/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/solo.io_client.go deleted file mode 100644 index 18b059b..0000000 --- a/pkg/storage/crd/client/clientset/versioned/typed/solo.io/v1/solo.io_client.go +++ /dev/null @@ -1,95 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1 - -import ( - "github.com/solo-io/sqoop/pkg/storage/crd/client/clientset/versioned/scheme" - v1 "github.com/solo-io/sqoop/pkg/storage/crd/solo.io/v1" - serializer "k8s.io/apimachinery/pkg/runtime/serializer" - rest "k8s.io/client-go/rest" -) - -type SqoopV1Interface interface { - RESTClient() rest.Interface - ResolverMapsGetter - SchemasGetter -} - -// SqoopV1Client is used to interact with features provided by the sqoop.solo.io group. -type SqoopV1Client struct { - restClient rest.Interface -} - -func (c *SqoopV1Client) ResolverMaps(namespace string) ResolverMapInterface { - return newResolverMaps(c, namespace) -} - -func (c *SqoopV1Client) Schemas(namespace string) SchemaInterface { - return newSchemas(c, namespace) -} - -// NewForConfig creates a new SqoopV1Client for the given config. -func NewForConfig(c *rest.Config) (*SqoopV1Client, error) { - config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } - client, err := rest.RESTClientFor(&config) - if err != nil { - return nil, err - } - return &SqoopV1Client{client}, nil -} - -// NewForConfigOrDie creates a new SqoopV1Client for the given config and -// panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *SqoopV1Client { - client, err := NewForConfig(c) - if err != nil { - panic(err) - } - return client -} - -// New creates a new SqoopV1Client for the given RESTClient. -func New(c rest.Interface) *SqoopV1Client { - return &SqoopV1Client{c} -} - -func setConfigDefaults(config *rest.Config) error { - gv := v1.SchemeGroupVersion - config.GroupVersion = &gv - config.APIPath = "/apis" - config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} - - if config.UserAgent == "" { - config.UserAgent = rest.DefaultKubernetesUserAgent() - } - - return nil -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *SqoopV1Client) RESTClient() rest.Interface { - if c == nil { - return nil - } - return c.restClient -} diff --git a/pkg/storage/crd/client/informers/externalversions/factory.go b/pkg/storage/crd/client/informers/externalversions/factory.go deleted file mode 100644 index 4d68b46..0000000 --- a/pkg/storage/crd/client/informers/externalversions/factory.go +++ /dev/null @@ -1,180 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package externalversions - -import ( - reflect "reflect" - sync "sync" - time "time" - - versioned "github.com/solo-io/sqoop/pkg/storage/crd/client/clientset/versioned" - internalinterfaces "github.com/solo-io/sqoop/pkg/storage/crd/client/informers/externalversions/internalinterfaces" - solo_io "github.com/solo-io/sqoop/pkg/storage/crd/client/informers/externalversions/solo.io" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - schema "k8s.io/apimachinery/pkg/runtime/schema" - cache "k8s.io/client-go/tools/cache" -) - -// SharedInformerOption defines the functional option type for SharedInformerFactory. -type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory - -type sharedInformerFactory struct { - client versioned.Interface - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc - lock sync.Mutex - defaultResync time.Duration - customResync map[reflect.Type]time.Duration - - informers map[reflect.Type]cache.SharedIndexInformer - // startedInformers is used for tracking which informers have been started. - // This allows Start() to be called multiple times safely. - startedInformers map[reflect.Type]bool -} - -// WithCustomResyncConfig sets a custom resync period for the specified informer types. -func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption { - return func(factory *sharedInformerFactory) *sharedInformerFactory { - for k, v := range resyncConfig { - factory.customResync[reflect.TypeOf(k)] = v - } - return factory - } -} - -// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory. -func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption { - return func(factory *sharedInformerFactory) *sharedInformerFactory { - factory.tweakListOptions = tweakListOptions - return factory - } -} - -// WithNamespace limits the SharedInformerFactory to the specified namespace. -func WithNamespace(namespace string) SharedInformerOption { - return func(factory *sharedInformerFactory) *sharedInformerFactory { - factory.namespace = namespace - return factory - } -} - -// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. -func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory { - return NewSharedInformerFactoryWithOptions(client, defaultResync) -} - -// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. -// Listers obtained via this SharedInformerFactory will be subject to the same filters -// as specified here. -// Deprecated: Please use NewSharedInformerFactoryWithOptions instead -func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { - return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) -} - -// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options. -func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory { - factory := &sharedInformerFactory{ - client: client, - namespace: v1.NamespaceAll, - defaultResync: defaultResync, - informers: make(map[reflect.Type]cache.SharedIndexInformer), - startedInformers: make(map[reflect.Type]bool), - customResync: make(map[reflect.Type]time.Duration), - } - - // Apply all options - for _, opt := range options { - factory = opt(factory) - } - - return factory -} - -// Start initializes all requested informers. -func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { - f.lock.Lock() - defer f.lock.Unlock() - - for informerType, informer := range f.informers { - if !f.startedInformers[informerType] { - go informer.Run(stopCh) - f.startedInformers[informerType] = true - } - } -} - -// WaitForCacheSync waits for all started informers' cache were synced. -func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { - informers := func() map[reflect.Type]cache.SharedIndexInformer { - f.lock.Lock() - defer f.lock.Unlock() - - informers := map[reflect.Type]cache.SharedIndexInformer{} - for informerType, informer := range f.informers { - if f.startedInformers[informerType] { - informers[informerType] = informer - } - } - return informers - }() - - res := map[reflect.Type]bool{} - for informType, informer := range informers { - res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) - } - return res -} - -// InternalInformerFor returns the SharedIndexInformer for obj using an internal -// client. -func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { - f.lock.Lock() - defer f.lock.Unlock() - - informerType := reflect.TypeOf(obj) - informer, exists := f.informers[informerType] - if exists { - return informer - } - - resyncPeriod, exists := f.customResync[informerType] - if !exists { - resyncPeriod = f.defaultResync - } - - informer = newFunc(f.client, resyncPeriod) - f.informers[informerType] = informer - - return informer -} - -// SharedInformerFactory provides shared informers for resources in all known -// API group versions. -type SharedInformerFactory interface { - internalinterfaces.SharedInformerFactory - ForResource(resource schema.GroupVersionResource) (GenericInformer, error) - WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool - - Sqoop() solo_io.Interface -} - -func (f *sharedInformerFactory) Sqoop() solo_io.Interface { - return solo_io.New(f, f.namespace, f.tweakListOptions) -} diff --git a/pkg/storage/crd/client/informers/externalversions/generic.go b/pkg/storage/crd/client/informers/externalversions/generic.go deleted file mode 100644 index 547d3bd..0000000 --- a/pkg/storage/crd/client/informers/externalversions/generic.go +++ /dev/null @@ -1,64 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package externalversions - -import ( - "fmt" - - v1 "github.com/solo-io/sqoop/pkg/storage/crd/solo.io/v1" - schema "k8s.io/apimachinery/pkg/runtime/schema" - cache "k8s.io/client-go/tools/cache" -) - -// GenericInformer is type of SharedIndexInformer which will locate and delegate to other -// sharedInformers based on type -type GenericInformer interface { - Informer() cache.SharedIndexInformer - Lister() cache.GenericLister -} - -type genericInformer struct { - informer cache.SharedIndexInformer - resource schema.GroupResource -} - -// Informer returns the SharedIndexInformer. -func (f *genericInformer) Informer() cache.SharedIndexInformer { - return f.informer -} - -// Lister returns the GenericLister. -func (f *genericInformer) Lister() cache.GenericLister { - return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) -} - -// ForResource gives generic access to a shared informer of the matching type -// TODO extend this to unknown resources with a client pool -func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { - switch resource { - // Group=sqoop.solo.io, Version=v1 - case v1.SchemeGroupVersion.WithResource("resolvermaps"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Sqoop().V1().ResolverMaps().Informer()}, nil - case v1.SchemeGroupVersion.WithResource("schemas"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Sqoop().V1().Schemas().Informer()}, nil - - } - - return nil, fmt.Errorf("no informer found for %v", resource) -} diff --git a/pkg/storage/crd/client/informers/externalversions/internalinterfaces/factory_interfaces.go b/pkg/storage/crd/client/informers/externalversions/internalinterfaces/factory_interfaces.go deleted file mode 100644 index 277871e..0000000 --- a/pkg/storage/crd/client/informers/externalversions/internalinterfaces/factory_interfaces.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package internalinterfaces - -import ( - time "time" - - versioned "github.com/solo-io/sqoop/pkg/storage/crd/client/clientset/versioned" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - cache "k8s.io/client-go/tools/cache" -) - -type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer - -// SharedInformerFactory a small interface to allow for adding an informer without an import cycle -type SharedInformerFactory interface { - Start(stopCh <-chan struct{}) - InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer -} - -type TweakListOptionsFunc func(*v1.ListOptions) diff --git a/pkg/storage/crd/client/informers/externalversions/solo.io/interface.go b/pkg/storage/crd/client/informers/externalversions/solo.io/interface.go deleted file mode 100644 index 50794fe..0000000 --- a/pkg/storage/crd/client/informers/externalversions/solo.io/interface.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package sqoop - -import ( - internalinterfaces "github.com/solo-io/sqoop/pkg/storage/crd/client/informers/externalversions/internalinterfaces" - v1 "github.com/solo-io/sqoop/pkg/storage/crd/client/informers/externalversions/solo.io/v1" -) - -// Interface provides access to each of this group's versions. -type Interface interface { - // V1 provides access to shared informers for resources in V1. - V1() v1.Interface -} - -type group struct { - factory internalinterfaces.SharedInformerFactory - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { - return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} -} - -// V1 returns a new v1.Interface. -func (g *group) V1() v1.Interface { - return v1.New(g.factory, g.namespace, g.tweakListOptions) -} diff --git a/pkg/storage/crd/client/informers/externalversions/solo.io/v1/interface.go b/pkg/storage/crd/client/informers/externalversions/solo.io/v1/interface.go deleted file mode 100644 index 6747349..0000000 --- a/pkg/storage/crd/client/informers/externalversions/solo.io/v1/interface.go +++ /dev/null @@ -1,52 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1 - -import ( - internalinterfaces "github.com/solo-io/sqoop/pkg/storage/crd/client/informers/externalversions/internalinterfaces" -) - -// Interface provides access to all the informers in this group version. -type Interface interface { - // ResolverMaps returns a ResolverMapInformer. - ResolverMaps() ResolverMapInformer - // Schemas returns a SchemaInformer. - Schemas() SchemaInformer -} - -type version struct { - factory internalinterfaces.SharedInformerFactory - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { - return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} -} - -// ResolverMaps returns a ResolverMapInformer. -func (v *version) ResolverMaps() ResolverMapInformer { - return &resolverMapInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} - -// Schemas returns a SchemaInformer. -func (v *version) Schemas() SchemaInformer { - return &schemaInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} diff --git a/pkg/storage/crd/client/informers/externalversions/solo.io/v1/resolvermap.go b/pkg/storage/crd/client/informers/externalversions/solo.io/v1/resolvermap.go deleted file mode 100644 index ddb0c57..0000000 --- a/pkg/storage/crd/client/informers/externalversions/solo.io/v1/resolvermap.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1 - -import ( - time "time" - - versioned "github.com/solo-io/sqoop/pkg/storage/crd/client/clientset/versioned" - internalinterfaces "github.com/solo-io/sqoop/pkg/storage/crd/client/informers/externalversions/internalinterfaces" - v1 "github.com/solo-io/sqoop/pkg/storage/crd/client/listers/solo.io/v1" - solo_io_v1 "github.com/solo-io/sqoop/pkg/storage/crd/solo.io/v1" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// ResolverMapInformer provides access to a shared informer and lister for -// ResolverMaps. -type ResolverMapInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1.ResolverMapLister -} - -type resolverMapInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewResolverMapInformer constructs a new informer for ResolverMap type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewResolverMapInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredResolverMapInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredResolverMapInformer constructs a new informer for ResolverMap type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredResolverMapInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options meta_v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.SqoopV1().ResolverMaps(namespace).List(options) - }, - WatchFunc: func(options meta_v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.SqoopV1().ResolverMaps(namespace).Watch(options) - }, - }, - &solo_io_v1.ResolverMap{}, - resyncPeriod, - indexers, - ) -} - -func (f *resolverMapInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredResolverMapInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *resolverMapInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&solo_io_v1.ResolverMap{}, f.defaultInformer) -} - -func (f *resolverMapInformer) Lister() v1.ResolverMapLister { - return v1.NewResolverMapLister(f.Informer().GetIndexer()) -} diff --git a/pkg/storage/crd/client/informers/externalversions/solo.io/v1/schema.go b/pkg/storage/crd/client/informers/externalversions/solo.io/v1/schema.go deleted file mode 100644 index 9d3d012..0000000 --- a/pkg/storage/crd/client/informers/externalversions/solo.io/v1/schema.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1 - -import ( - time "time" - - versioned "github.com/solo-io/sqoop/pkg/storage/crd/client/clientset/versioned" - internalinterfaces "github.com/solo-io/sqoop/pkg/storage/crd/client/informers/externalversions/internalinterfaces" - v1 "github.com/solo-io/sqoop/pkg/storage/crd/client/listers/solo.io/v1" - solo_io_v1 "github.com/solo-io/sqoop/pkg/storage/crd/solo.io/v1" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// SchemaInformer provides access to a shared informer and lister for -// Schemas. -type SchemaInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1.SchemaLister -} - -type schemaInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewSchemaInformer constructs a new informer for Schema type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewSchemaInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredSchemaInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredSchemaInformer constructs a new informer for Schema type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredSchemaInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options meta_v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.SqoopV1().Schemas(namespace).List(options) - }, - WatchFunc: func(options meta_v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.SqoopV1().Schemas(namespace).Watch(options) - }, - }, - &solo_io_v1.Schema{}, - resyncPeriod, - indexers, - ) -} - -func (f *schemaInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredSchemaInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *schemaInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&solo_io_v1.Schema{}, f.defaultInformer) -} - -func (f *schemaInformer) Lister() v1.SchemaLister { - return v1.NewSchemaLister(f.Informer().GetIndexer()) -} diff --git a/pkg/storage/crd/client/listers/solo.io/v1/expansion_generated.go b/pkg/storage/crd/client/listers/solo.io/v1/expansion_generated.go deleted file mode 100644 index 6e4686b..0000000 --- a/pkg/storage/crd/client/listers/solo.io/v1/expansion_generated.go +++ /dev/null @@ -1,35 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1 - -// ResolverMapListerExpansion allows custom methods to be added to -// ResolverMapLister. -type ResolverMapListerExpansion interface{} - -// ResolverMapNamespaceListerExpansion allows custom methods to be added to -// ResolverMapNamespaceLister. -type ResolverMapNamespaceListerExpansion interface{} - -// SchemaListerExpansion allows custom methods to be added to -// SchemaLister. -type SchemaListerExpansion interface{} - -// SchemaNamespaceListerExpansion allows custom methods to be added to -// SchemaNamespaceLister. -type SchemaNamespaceListerExpansion interface{} diff --git a/pkg/storage/crd/client/listers/solo.io/v1/resolvermap.go b/pkg/storage/crd/client/listers/solo.io/v1/resolvermap.go deleted file mode 100644 index 946809a..0000000 --- a/pkg/storage/crd/client/listers/solo.io/v1/resolvermap.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1 - -import ( - v1 "github.com/solo-io/sqoop/pkg/storage/crd/solo.io/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// ResolverMapLister helps list ResolverMaps. -type ResolverMapLister interface { - // List lists all ResolverMaps in the indexer. - List(selector labels.Selector) (ret []*v1.ResolverMap, err error) - // ResolverMaps returns an object that can list and get ResolverMaps. - ResolverMaps(namespace string) ResolverMapNamespaceLister - ResolverMapListerExpansion -} - -// resolverMapLister implements the ResolverMapLister interface. -type resolverMapLister struct { - indexer cache.Indexer -} - -// NewResolverMapLister returns a new ResolverMapLister. -func NewResolverMapLister(indexer cache.Indexer) ResolverMapLister { - return &resolverMapLister{indexer: indexer} -} - -// List lists all ResolverMaps in the indexer. -func (s *resolverMapLister) List(selector labels.Selector) (ret []*v1.ResolverMap, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1.ResolverMap)) - }) - return ret, err -} - -// ResolverMaps returns an object that can list and get ResolverMaps. -func (s *resolverMapLister) ResolverMaps(namespace string) ResolverMapNamespaceLister { - return resolverMapNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// ResolverMapNamespaceLister helps list and get ResolverMaps. -type ResolverMapNamespaceLister interface { - // List lists all ResolverMaps in the indexer for a given namespace. - List(selector labels.Selector) (ret []*v1.ResolverMap, err error) - // Get retrieves the ResolverMap from the indexer for a given namespace and name. - Get(name string) (*v1.ResolverMap, error) - ResolverMapNamespaceListerExpansion -} - -// resolverMapNamespaceLister implements the ResolverMapNamespaceLister -// interface. -type resolverMapNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all ResolverMaps in the indexer for a given namespace. -func (s resolverMapNamespaceLister) List(selector labels.Selector) (ret []*v1.ResolverMap, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1.ResolverMap)) - }) - return ret, err -} - -// Get retrieves the ResolverMap from the indexer for a given namespace and name. -func (s resolverMapNamespaceLister) Get(name string) (*v1.ResolverMap, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1.Resource("resolvermap"), name) - } - return obj.(*v1.ResolverMap), nil -} diff --git a/pkg/storage/crd/client/listers/solo.io/v1/schema.go b/pkg/storage/crd/client/listers/solo.io/v1/schema.go deleted file mode 100644 index 9c1d14c..0000000 --- a/pkg/storage/crd/client/listers/solo.io/v1/schema.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1 - -import ( - v1 "github.com/solo-io/sqoop/pkg/storage/crd/solo.io/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// SchemaLister helps list Schemas. -type SchemaLister interface { - // List lists all Schemas in the indexer. - List(selector labels.Selector) (ret []*v1.Schema, err error) - // Schemas returns an object that can list and get Schemas. - Schemas(namespace string) SchemaNamespaceLister - SchemaListerExpansion -} - -// schemaLister implements the SchemaLister interface. -type schemaLister struct { - indexer cache.Indexer -} - -// NewSchemaLister returns a new SchemaLister. -func NewSchemaLister(indexer cache.Indexer) SchemaLister { - return &schemaLister{indexer: indexer} -} - -// List lists all Schemas in the indexer. -func (s *schemaLister) List(selector labels.Selector) (ret []*v1.Schema, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1.Schema)) - }) - return ret, err -} - -// Schemas returns an object that can list and get Schemas. -func (s *schemaLister) Schemas(namespace string) SchemaNamespaceLister { - return schemaNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// SchemaNamespaceLister helps list and get Schemas. -type SchemaNamespaceLister interface { - // List lists all Schemas in the indexer for a given namespace. - List(selector labels.Selector) (ret []*v1.Schema, err error) - // Get retrieves the Schema from the indexer for a given namespace and name. - Get(name string) (*v1.Schema, error) - SchemaNamespaceListerExpansion -} - -// schemaNamespaceLister implements the SchemaNamespaceLister -// interface. -type schemaNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all Schemas in the indexer for a given namespace. -func (s schemaNamespaceLister) List(selector labels.Selector) (ret []*v1.Schema, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1.Schema)) - }) - return ret, err -} - -// Get retrieves the Schema from the indexer for a given namespace and name. -func (s schemaNamespaceLister) Get(name string) (*v1.Schema, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1.Resource("schema"), name) - } - return obj.(*v1.Schema), nil -} diff --git a/pkg/storage/crd/client_template.go.tmpl b/pkg/storage/crd/client_template.go.tmpl deleted file mode 100644 index 9a31518..0000000 --- a/pkg/storage/crd/client_template.go.tmpl +++ /dev/null @@ -1,193 +0,0 @@ -package crd - -import ( - "time" - - "github.com/pkg/errors" - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/storage" - crdclientset "github.com/solo-io/sqoop/pkg/storage/crd/client/clientset/versioned" - crdv1 "github.com/solo-io/sqoop/pkg/storage/crd/solo.io/v1" - apiexts "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" - - "github.com/solo-io/gloo/pkg/log" - "github.com/solo-io/sqoop/pkg/storage/crud" - kuberrs "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/client-go/tools/cache" -) - -type {{ .LowercasePluralName }}Client struct { - crds crdclientset.Interface - apiexts apiexts.Interface - // write and read objects to this namespace if not specified on the SqoopObjects - namespace string - syncFrequency time.Duration -} - -func (c *{{ .LowercasePluralName }}Client) Create(item *v1.{{ .UppercaseName }}) (*v1.{{ .UppercaseName }}, error) { - return c.createOrUpdate{{ .UppercaseName }}Crd(item, crud.OperationCreate) -} - -func (c *{{ .LowercasePluralName }}Client) Update(item *v1.{{ .UppercaseName }}) (*v1.{{ .UppercaseName }}, error) { - return c.createOrUpdate{{ .UppercaseName }}Crd(item, crud.OperationUpdate) -} - -func (c *{{ .LowercasePluralName }}Client) Delete(name string) error { - return c.crds.SqoopV1().{{ .UppercasePluralName }}(c.namespace).Delete(name, nil) -} - -func (c *{{ .LowercasePluralName }}Client) Get(name string) (*v1.{{ .UppercaseName }}, error) { - crd{{ .UppercaseName }}, err := c.crds.SqoopV1().{{ .UppercasePluralName }}(c.namespace).Get(name, metav1.GetOptions{}) - if err != nil { - return nil, errors.Wrap(err, "failed performing get api request") - } - var returned{{ .UppercaseName }} v1.{{ .UppercaseName }} - if err := ConfigObjectFromCrd( - crd{{ .UppercaseName }}.ObjectMeta, - crd{{ .UppercaseName }}.Spec, - crd{{ .UppercaseName }}.Status, - &returned{{ .UppercaseName }}); err != nil { - return nil, errors.Wrap(err, "converting returned crd to {{ .LowercaseName }}") - } - return &returned{{ .UppercaseName }}, nil -} - -func (c *{{ .LowercasePluralName }}Client) List() ([]*v1.{{ .UppercaseName }}, error) { - crdList, err := c.crds.SqoopV1().{{ .UppercasePluralName }}(c.namespace).List(metav1.ListOptions{}) - if err != nil { - return nil, errors.Wrap(err, "failed performing list api request") - } - var returned{{ .UppercasePluralName }} []*v1.{{ .UppercaseName }} - for _, crd{{ .UppercaseName }} := range crdList.Items { - var returned{{ .UppercaseName }} v1.{{ .UppercaseName }} - if err := ConfigObjectFromCrd( - crd{{ .UppercaseName }}.ObjectMeta, - crd{{ .UppercaseName }}.Spec, - crd{{ .UppercaseName }}.Status, - &returned{{ .UppercaseName }}); err != nil { - return nil, errors.Wrap(err, "converting returned crd to {{ .LowercaseName }}") - } - returned{{ .UppercasePluralName }} = append(returned{{ .UppercasePluralName }}, &returned{{ .UppercaseName }}) - } - return returned{{ .UppercasePluralName }}, nil -} - -func (u *{{ .LowercasePluralName }}Client) Watch(handlers ...storage.{{ .UppercaseName }}EventHandler) (*storage.Watcher, error) { - lw := cache.NewListWatchFromClient(u.crds.SqoopV1().RESTClient(), crdv1.{{ .UppercaseName }}CRD.Plural, u.namespace, fields.Everything()) - sw := cache.NewSharedInformer(lw, new(crdv1.{{ .UppercaseName }}), u.syncFrequency) - for _, h := range handlers { - sw.AddEventHandler(&{{ .LowercaseName }}EventHandler{handler: h, store: sw.GetStore()}) - } - return storage.NewWatcher(func(stop <-chan struct{}, _ chan error) { - sw.Run(stop) - }), nil -} - -func (c *{{ .LowercasePluralName }}Client) createOrUpdate{{ .UppercaseName }}Crd({{ .LowercaseName }} *v1.{{ .UppercaseName }}, op crud.Operation) (*v1.{{ .UppercaseName }}, error) { - {{ .LowercaseName }}Crd, err := ConfigObjectToCrd(c.namespace, {{ .LowercaseName }}) - if err != nil { - return nil, errors.Wrap(err, "converting sqoop object to crd") - } - {{ .LowercasePluralName }} := c.crds.SqoopV1().{{ .UppercasePluralName }}({{ .LowercaseName }}Crd.GetNamespace()) - var returnedCrd *crdv1.{{ .UppercaseName }} - switch op { - case crud.OperationCreate: - returnedCrd, err = {{ .LowercasePluralName }}.Create({{ .LowercaseName }}Crd.(*crdv1.{{ .UppercaseName }})) - if err != nil { - if kuberrs.IsAlreadyExists(err) { - return nil, storage.NewAlreadyExistsErr(err) - } - return nil, errors.Wrap(err, "kubernetes create api request") - } - case crud.OperationUpdate: - // need to make sure we preserve labels - currentCrd, err := {{ .LowercasePluralName }}.Get({{ .LowercaseName }}Crd.GetName(), metav1.GetOptions{ResourceVersion: {{ .LowercaseName }}Crd.GetResourceVersion()}) - if err != nil { - return nil, errors.Wrap(err, "kubernetes get api request") - } - // copy labels - {{ .LowercaseName }}Crd.SetLabels(currentCrd.Labels) - returnedCrd, err = {{ .LowercasePluralName }}.Update({{ .LowercaseName }}Crd.(*crdv1.{{ .UppercaseName }})) - if err != nil { - return nil, errors.Wrap(err, "kubernetes update api request") - } - } - var returned{{ .UppercaseName }} v1.{{ .UppercaseName }} - if err := ConfigObjectFromCrd( - returnedCrd.ObjectMeta, - returnedCrd.Spec, - returnedCrd.Status, - &returned{{ .UppercaseName }}); err != nil { - return nil, errors.Wrap(err, "converting returned crd to {{ .LowercaseName }}") - } - return &returned{{ .UppercaseName }}, nil -} - -// implements the kubernetes ResourceEventHandler interface -type {{ .LowercaseName }}EventHandler struct { - handler storage.{{ .UppercaseName }}EventHandler - store cache.Store -} - -func (eh *{{ .LowercaseName }}EventHandler) getUpdatedList() []*v1.{{ .UppercaseName }} { - updatedList := eh.store.List() - var updated{{ .UppercaseName }}List []*v1.{{ .UppercaseName }} - for _, updated := range updatedList { - {{ .LowercaseName }}Crd, ok := updated.(*crdv1.{{ .UppercaseName }}) - if !ok { - continue - } - var returned{{ .UppercaseName }} v1.{{ .UppercaseName }} - if err := ConfigObjectFromCrd( - {{ .LowercaseName }}Crd.ObjectMeta, - {{ .LowercaseName }}Crd.Spec, - {{ .LowercaseName }}Crd.Status, - &returned{{ .UppercaseName }}); err != nil { - log.Warnf("watch event: %v", errors.Wrap(err, "converting returned crd to {{ .LowercaseName }}")) - } - updated{{ .UppercaseName }}List = append(updated{{ .UppercaseName }}List, &returned{{ .UppercaseName }}) - } - return updated{{ .UppercaseName }}List -} - -func convert{{ .UppercaseName }}(obj interface{}) (*v1.{{ .UppercaseName }}, bool) { - {{ .LowercaseName }}Crd, ok := obj.(*crdv1.{{ .UppercaseName }}) - if !ok { - return nil, ok - } - var returned{{ .UppercaseName }} v1.{{ .UppercaseName }} - if err := ConfigObjectFromCrd( - {{ .LowercaseName }}Crd.ObjectMeta, - {{ .LowercaseName }}Crd.Spec, - {{ .LowercaseName }}Crd.Status, - &returned{{ .UppercaseName }}); err != nil { - log.Warnf("watch event: %v", errors.Wrap(err, "converting returned crd to {{ .LowercaseName }}")) - return nil, false - } - return &returned{{ .UppercaseName }}, true -} - -func (eh *{{ .LowercaseName }}EventHandler) OnAdd(obj interface{}) { - {{ .LowercaseName }}, ok := convert{{ .UppercaseName }}(obj) - if !ok { - return - } - eh.handler.OnAdd(eh.getUpdatedList(), {{ .LowercaseName }}) -} -func (eh *{{ .LowercaseName }}EventHandler) OnUpdate(_, newObj interface{}) { - new{{ .UppercaseName }}, ok := convert{{ .UppercaseName }}(newObj) - if !ok { - return - } - eh.handler.OnUpdate(eh.getUpdatedList(), new{{ .UppercaseName }}) -} - -func (eh *{{ .LowercaseName }}EventHandler) OnDelete(obj interface{}) { - {{ .LowercaseName }}, ok := convert{{ .UppercaseName }}(obj) - if !ok { - return - } - eh.handler.OnDelete(eh.getUpdatedList(), {{ .LowercaseName }}) -} diff --git a/pkg/storage/crd/conversion.go b/pkg/storage/crd/conversion.go deleted file mode 100644 index b9a3408..0000000 --- a/pkg/storage/crd/conversion.go +++ /dev/null @@ -1,101 +0,0 @@ -package crd - -import ( - "github.com/gogo/protobuf/proto" - "github.com/pkg/errors" - gloov1 "github.com/solo-io/gloo/pkg/api/types/v1" - "github.com/solo-io/gloo/pkg/protoutil" - "github.com/solo-io/sqoop/pkg/api/types/v1" - crdv1 "github.com/solo-io/sqoop/pkg/storage/crd/solo.io/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func ConfigObjectToCrd(namespace string, item gloov1.ConfigObject) (metav1.Object, error) { - name := item.GetName() - var ( - status *gloov1.Status - ok bool - ) - if item.GetStatus() != nil { - status, ok = proto.Clone(item.GetStatus()).(*gloov1.Status) - if !ok { - return nil, errors.New("internal error: output of proto.Clone was not expected type") - } - } - var ( - resourceVersion string - annotations map[string]string - ) - if item.GetMetadata() != nil { - resourceVersion = item.GetMetadata().ResourceVersion - if item.GetMetadata().Namespace != "" { - namespace = item.GetMetadata().Namespace - } - annotations = item.GetMetadata().Annotations - } - - // TODO: merge this with ConfigObjectToCRD in Gloo - // clone and remove fields - clone, ok := proto.Clone(item).(gloov1.ConfigObject) - if !ok { - return nil, errors.New("internal error: output of proto.Clone was not expected type") - } - clone.SetMetadata(nil) - clone.SetName("") - clone.SetStatus(nil) - - spec, err := protoutil.MarshalMap(clone) - if err != nil { - return nil, errors.Wrap(err, "failed to convert proto config object to map[string]interface{}") - } - copySpec := crdv1.Spec(spec) - - meta := metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - ResourceVersion: resourceVersion, - Annotations: annotations, - } - - var crdObject metav1.Object - - switch item.(type) { - case *v1.Schema: - crdObject = &crdv1.Schema{ - ObjectMeta: meta, - Status: status, - Spec: ©Spec, - } - case *v1.ResolverMap: - crdObject = &crdv1.ResolverMap{ - ObjectMeta: meta, - Status: status, - Spec: ©Spec, - } - default: - panic(errors.Errorf("unknown type: %v", item)) - } - - return crdObject, nil -} - -func ConfigObjectFromCrd(objectMeta metav1.ObjectMeta, - spec *crdv1.Spec, - status *gloov1.Status, - item gloov1.ConfigObject) error { - if spec != nil { - err := protoutil.UnmarshalMap(*spec, item) - if err != nil { - return errors.Wrap(err, "failed to convert crd spec to config object") - } - } - // add removed fields to the internal object - item.SetName(objectMeta.Name) - item.SetMetadata(&gloov1.Metadata{ - ResourceVersion: objectMeta.ResourceVersion, - Namespace: objectMeta.Namespace, - Annotations: objectMeta.Annotations, - }) - item.SetStatus(status) - return nil -} diff --git a/pkg/storage/crd/conversion_test.go b/pkg/storage/crd/conversion_test.go deleted file mode 100644 index d205f1a..0000000 --- a/pkg/storage/crd/conversion_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package crd_test - -import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - gloov1 "github.com/solo-io/gloo/pkg/api/types/v1" - . "github.com/solo-io/sqoop/pkg/storage/crd" - crdv1 "github.com/solo-io/sqoop/pkg/storage/crd/solo.io/v1" -) - -var _ = Describe("Conversion", func() { - Describe("SchemaToCrd", func() { - It("Converts a gloo schema to crd", func() { - schema := NewTestSchema1() - annotations := map[string]string{"foo": "bar"} - schema.Metadata = &gloov1.Metadata{ - Annotations: annotations, - } - upCrd, err := ConfigObjectToCrd("foo", schema) - Expect(err).NotTo(HaveOccurred()) - Expect(upCrd.GetName()).To(Equal(schema.Name)) - Expect(upCrd.GetNamespace()).To(Equal("foo")) - Expect(upCrd.GetAnnotations()).To(Equal(annotations)) - spec := *upCrd.(*crdv1.Schema).Spec - // removed parts - Expect(spec["name"]).To(BeNil()) - Expect(spec["metadata"]).To(BeNil()) - Expect(spec["status"]).To(BeNil()) - Expect(spec["annotations"]).To(BeNil()) - - // shifted parts - Expect(spec["inline_schema"]).To(Equal(schema.InlineSchema)) - }) - }) - Describe("ResolverMapToCrd", func() { - It("Converts a gloo resolverMap to crd", func() { - resolverMap := NewTestResolverMap("foo") - annotations := map[string]string{"foo": "bar"} - resolverMap.Metadata = &gloov1.Metadata{ - Annotations: annotations, - } - resolverMapCrd, err := ConfigObjectToCrd("foo", resolverMap) - Expect(err).NotTo(HaveOccurred()) - Expect(resolverMapCrd.GetName()).To(Equal(resolverMap.Name)) - Expect(resolverMapCrd.GetNamespace()).To(Equal("foo")) - Expect(resolverMapCrd.GetAnnotations()).To(Equal(annotations)) - spec := *resolverMapCrd.(*crdv1.ResolverMap).Spec - // removed parts - Expect(spec["name"]).To(BeNil()) - Expect(spec["metadata"]).To(BeNil()) - Expect(spec["status"]).To(BeNil()) - Expect(spec["annotations"]).To(BeNil()) - - }) - }) -}) diff --git a/pkg/storage/crd/crd_storage_client.go b/pkg/storage/crd/crd_storage_client.go deleted file mode 100644 index a1349e5..0000000 --- a/pkg/storage/crd/crd_storage_client.go +++ /dev/null @@ -1,112 +0,0 @@ -package crd - -import ( - "fmt" - "time" - - corev1 "k8s.io/api/core/v1" - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" - apiexts "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - - "github.com/solo-io/gloo/pkg/log" - "github.com/solo-io/gloo/pkg/storage/crd" - "github.com/solo-io/sqoop/pkg/storage" - crdclientset "github.com/solo-io/sqoop/pkg/storage/crd/client/clientset/versioned" - crdv1 "github.com/solo-io/sqoop/pkg/storage/crd/solo.io/v1" -) - -//go:generate go run ${GOPATH}/src/github.com/solo-io/sqoop/pkg/storage/generate/generate_clients.go -f ${GOPATH}/src/github.com/solo-io/sqoop/pkg/storage/crd/client_template.go.tmpl -o ${GOPATH}/src/github.com/solo-io/sqoop/pkg/storage/crd/ -type Client struct { - v1 *v1client -} - -func NewStorage(cfg *rest.Config, namespace string, syncFrequency time.Duration) (storage.Interface, error) { - if namespace == "" { - namespace = crd.GlooDefaultNamespace - } - crdClient, err := crdclientset.NewForConfig(cfg) - if err != nil { - return nil, err - } - apiextClient, err := apiexts.NewForConfig(cfg) - if err != nil { - return nil, err - } - kubeClient, err := kubernetes.NewForConfig(cfg) - if err != nil { - return nil, err - } - return &Client{ - v1: &v1client{ - schemas: &schemasClient{ - crds: crdClient, - namespace: namespace, - syncFrequency: syncFrequency, - }, - resolverMaps: &resolverMapsClient{ - crds: crdClient, - namespace: namespace, - syncFrequency: syncFrequency, - }, - apiexts: apiextClient, - kubeclient: kubeClient, - namespace: namespace, - }, - }, nil -} - -func (c *Client) V1() storage.V1 { - return c.v1 -} - -type v1client struct { - apiexts apiexts.Interface - kubeclient kubernetes.Interface - schemas *schemasClient - resolverMaps *resolverMapsClient - namespace string -} - -func (c *v1client) Register() error { - // create namespace if it doesnt exist - if _, err := c.kubeclient.CoreV1().Namespaces().Create(&corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: c.namespace, - }, - }); err != nil && !apierrors.IsAlreadyExists(err) { - return fmt.Errorf("failed to create namespace %v: %v", c.namespace, err) - } - - for _, crd := range crdv1.KnownCRDs { - toRegister := &v1beta1.CustomResourceDefinition{ - ObjectMeta: metav1.ObjectMeta{Name: crd.FullName()}, - Spec: v1beta1.CustomResourceDefinitionSpec{ - Group: crd.Group, - Version: crd.Version, - Scope: v1beta1.NamespaceScoped, - Names: v1beta1.CustomResourceDefinitionNames{ - Plural: crd.Plural, - Kind: crd.Kind, - ShortNames: []string{crd.ShortName}, - }, - }, - } - log.Debugf("registering crd %v", crd) - if _, err := c.apiexts.ApiextensionsV1beta1().CustomResourceDefinitions().Create(toRegister); err != nil && !apierrors.IsAlreadyExists(err) { - return fmt.Errorf("failed to create crd: %v", err) - } - } - return nil -} - -func (c *v1client) Schemas() storage.Schemas { - return c.schemas -} - -func (c *v1client) ResolverMaps() storage.ResolverMaps { - return c.resolverMaps -} diff --git a/pkg/storage/crd/crd_storage_client_test.go b/pkg/storage/crd/crd_storage_client_test.go deleted file mode 100644 index b78d4f7..0000000 --- a/pkg/storage/crd/crd_storage_client_test.go +++ /dev/null @@ -1,246 +0,0 @@ -package crd_test - -import ( - "os" - "path/filepath" - - "time" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - gloov1 "github.com/solo-io/gloo/pkg/api/types/v1" - "github.com/solo-io/gloo/pkg/log" - . "github.com/solo-io/gloo/test/helpers" - "github.com/solo-io/sqoop/pkg/api/types/v1" - . "github.com/solo-io/sqoop/pkg/storage/crd" - crdv1 "github.com/solo-io/sqoop/pkg/storage/crd/solo.io/v1" - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" - apiexts "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/clientcmd" -) - -var _ = Describe("CrdStorageClient", func() { - if os.Getenv("RUN_KUBE_TESTS") != "1" { - log.Printf("This test creates kubernetes resources and is disabled by default. To enable, set RUN_KUBE_TESTS=1 in your env.") - return - } - var ( - masterUrl, kubeconfigPath string - namespace string - syncFreq = time.Minute - ) - BeforeEach(func() { - namespace = RandString(8) - err := SetupKubeForTest(namespace) - Must(err) - kubeconfigPath = filepath.Join(os.Getenv("HOME"), ".kube", "config") - masterUrl = "" - }) - AfterEach(func() { - TeardownKube(namespace) - }) - Describe("New", func() { - It("creates a new client without error", func() { - cfg, err := clientcmd.BuildConfigFromFlags(masterUrl, kubeconfigPath) - Expect(err).NotTo(HaveOccurred()) - _, err = NewStorage(cfg, namespace, syncFreq) - Expect(err).NotTo(HaveOccurred()) - }) - }) - Describe("Register", func() { - It("registers the crds", func() { - cfg, err := clientcmd.BuildConfigFromFlags(masterUrl, kubeconfigPath) - Expect(err).NotTo(HaveOccurred()) - client, err := NewStorage(cfg, namespace, syncFreq) - Expect(err).NotTo(HaveOccurred()) - err = client.V1().Register() - Expect(err).NotTo(HaveOccurred()) - apiextClient, err := apiexts.NewForConfig(cfg) - Expect(err).NotTo(HaveOccurred()) - crds, err := apiextClient.ApiextensionsV1beta1().CustomResourceDefinitions().List(metav1.ListOptions{}) - Expect(err).NotTo(HaveOccurred()) - for _, crdSchema := range crdv1.KnownCRDs { - var foundCrd *v1beta1.CustomResourceDefinition - for _, crd := range crds.Items { - if crd.Spec.Names.Kind == crdSchema.Kind { - foundCrd = &crd - break - } - } - // if crd wasnt found, err - Expect(foundCrd).NotTo(BeNil()) - - Expect(foundCrd.Spec.Version).To(Equal(crdSchema.Version)) - Expect(foundCrd.Spec.Group).To(Equal(crdSchema.Group)) - } - }) - }) - Describe("schemas", func() { - Describe("Create", func() { - It("creates a crd from the item", func() { - cfg, err := clientcmd.BuildConfigFromFlags(masterUrl, kubeconfigPath) - Expect(err).NotTo(HaveOccurred()) - client, err := NewStorage(cfg, namespace, syncFreq) - Expect(err).NotTo(HaveOccurred()) - err = client.V1().Register() - Expect(err).NotTo(HaveOccurred()) - schema := NewTestSchema1() - createdSchema, err := client.V1().Schemas().Create(schema) - Expect(err).NotTo(HaveOccurred()) - schema.Metadata = createdSchema.GetMetadata() - Expect(schema).To(Equal(createdSchema)) - }) - }) - Describe("Get", func() { - It("gets a crd from the name", func() { - cfg, err := clientcmd.BuildConfigFromFlags(masterUrl, kubeconfigPath) - Expect(err).NotTo(HaveOccurred()) - client, err := NewStorage(cfg, namespace, syncFreq) - Expect(err).NotTo(HaveOccurred()) - err = client.V1().Register() - Expect(err).NotTo(HaveOccurred()) - schema := NewTestSchema1() - _, err = client.V1().Schemas().Create(schema) - Expect(err).NotTo(HaveOccurred()) - created, err := client.V1().Schemas().Get(schema.Name) - Expect(err).NotTo(HaveOccurred()) - schema.Metadata = created.Metadata - Expect(created).To(Equal(schema)) - }) - }) - Describe("Update", func() { - It("updates a crd from the item", func() { - cfg, err := clientcmd.BuildConfigFromFlags(masterUrl, kubeconfigPath) - Expect(err).NotTo(HaveOccurred()) - client, err := NewStorage(cfg, namespace, syncFreq) - Expect(err).NotTo(HaveOccurred()) - err = client.V1().Register() - Expect(err).NotTo(HaveOccurred()) - schema := NewTestSchema1() - created, err := client.V1().Schemas().Create(schema) - Expect(err).NotTo(HaveOccurred()) - schema.Metadata = created.GetMetadata() - schema.ResolverMap = "something-else" - schema.Metadata.Annotations["just_for_this_test"] = "bar" - updated, err := client.V1().Schemas().Update(schema) - Expect(err).NotTo(HaveOccurred()) - Expect(updated.Metadata.Annotations).To(HaveKey("just_for_this_test")) - schema.Metadata = updated.GetMetadata() - Expect(updated).To(Equal(schema)) - }) - }) - Describe("Delete", func() { - It("deletes a crd from the name", func() { - cfg, err := clientcmd.BuildConfigFromFlags(masterUrl, kubeconfigPath) - Expect(err).NotTo(HaveOccurred()) - client, err := NewStorage(cfg, namespace, syncFreq) - Expect(err).NotTo(HaveOccurred()) - err = client.V1().Register() - Expect(err).NotTo(HaveOccurred()) - schema := NewTestSchema1() - _, err = client.V1().Schemas().Create(schema) - Expect(err).NotTo(HaveOccurred()) - err = client.V1().Schemas().Delete(schema.Name) - Expect(err).NotTo(HaveOccurred()) - _, err = client.V1().Schemas().Get(schema.Name) - Expect(err).To(HaveOccurred()) - }) - }) - }) - Describe("resolverMaps", func() { - Describe("Create", func() { - It("creates a crd from the item", func() { - cfg, err := clientcmd.BuildConfigFromFlags(masterUrl, kubeconfigPath) - Expect(err).NotTo(HaveOccurred()) - client, err := NewStorage(cfg, namespace, syncFreq) - Expect(err).NotTo(HaveOccurred()) - err = client.V1().Register() - Expect(err).NotTo(HaveOccurred()) - resolverMap := NewTestResolverMap("something") - createdSchema, err := client.V1().ResolverMaps().Create(resolverMap) - Expect(err).NotTo(HaveOccurred()) - resolverMap.Metadata = createdSchema.GetMetadata() - Expect(resolverMap).To(Equal(createdSchema)) - }) - }) - Describe("Get", func() { - It("gets a crd from the name", func() { - cfg, err := clientcmd.BuildConfigFromFlags(masterUrl, kubeconfigPath) - Expect(err).NotTo(HaveOccurred()) - client, err := NewStorage(cfg, namespace, syncFreq) - Expect(err).NotTo(HaveOccurred()) - err = client.V1().Register() - Expect(err).NotTo(HaveOccurred()) - resolverMap := NewTestResolverMap("something") - _, err = client.V1().ResolverMaps().Create(resolverMap) - Expect(err).NotTo(HaveOccurred()) - created, err := client.V1().ResolverMaps().Get(resolverMap.Name) - Expect(err).NotTo(HaveOccurred()) - resolverMap.Metadata = created.Metadata - Expect(created).To(Equal(resolverMap)) - }) - }) - Describe("Update", func() { - It("updates a crd from the item", func() { - cfg, err := clientcmd.BuildConfigFromFlags(masterUrl, kubeconfigPath) - Expect(err).NotTo(HaveOccurred()) - client, err := NewStorage(cfg, namespace, syncFreq) - Expect(err).NotTo(HaveOccurred()) - err = client.V1().Register() - Expect(err).NotTo(HaveOccurred()) - resolverMap := NewTestResolverMap("something") - created, err := client.V1().ResolverMaps().Create(resolverMap) - Expect(err).NotTo(HaveOccurred()) - // need to set resource ver - resolverMap.Metadata = created.GetMetadata() - resolverMap.Metadata.Annotations["just_for_this_test"] = "bar" - updated, err := client.V1().ResolverMaps().Update(resolverMap) - Expect(err).NotTo(HaveOccurred()) - Expect(updated.Metadata.Annotations).To(HaveKey("just_for_this_test")) - resolverMap.Metadata = updated.GetMetadata() - Expect(updated).To(Equal(resolverMap)) - }) - }) - Describe("Delete", func() { - It("deletes a crd from the name", func() { - cfg, err := clientcmd.BuildConfigFromFlags(masterUrl, kubeconfigPath) - Expect(err).NotTo(HaveOccurred()) - client, err := NewStorage(cfg, namespace, syncFreq) - Expect(err).NotTo(HaveOccurred()) - err = client.V1().Register() - Expect(err).NotTo(HaveOccurred()) - resolverMap := NewTestResolverMap("something") - _, err = client.V1().ResolverMaps().Create(resolverMap) - Expect(err).NotTo(HaveOccurred()) - err = client.V1().ResolverMaps().Delete(resolverMap.Name) - Expect(err).NotTo(HaveOccurred()) - _, err = client.V1().ResolverMaps().Get(resolverMap.Name) - Expect(err).To(HaveOccurred()) - }) - }) - }) -}) - -func NewTestSchema1() *v1.Schema { - return &v1.Schema{ - Name: "schema1", - ResolverMap: "resolvers", - InlineSchema: "SOMETHING", - Metadata: &gloov1.Metadata{ - Annotations: map[string]string{ - "foo": "bar", - }, - }, - } -} -func NewTestResolverMap(name string) *v1.ResolverMap { - return &v1.ResolverMap{ - Name: name, - Metadata: &gloov1.Metadata{ - Annotations: map[string]string{ - "foo": "bar", - }, - }, - } -} diff --git a/pkg/storage/crd/crd_suite_test.go b/pkg/storage/crd/crd_suite_test.go deleted file mode 100644 index 77d12a8..0000000 --- a/pkg/storage/crd/crd_suite_test.go +++ /dev/null @@ -1,15 +0,0 @@ -package crd - -import ( - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "github.com/solo-io/gloo/pkg/log" -) - -func TestCrd(t *testing.T) { - RegisterFailHandler(Fail) - log.DefaultOut = GinkgoWriter - RunSpecs(t, "Crd Suite") -} diff --git a/pkg/storage/crd/resolver_maps.go b/pkg/storage/crd/resolver_maps.go deleted file mode 100644 index 0bee8e4..0000000 --- a/pkg/storage/crd/resolver_maps.go +++ /dev/null @@ -1,193 +0,0 @@ -package crd - -import ( - "time" - - "github.com/pkg/errors" - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/storage" - crdclientset "github.com/solo-io/sqoop/pkg/storage/crd/client/clientset/versioned" - crdv1 "github.com/solo-io/sqoop/pkg/storage/crd/solo.io/v1" - apiexts "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" - - "github.com/solo-io/gloo/pkg/log" - "github.com/solo-io/sqoop/pkg/storage/crud" - kuberrs "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/client-go/tools/cache" -) - -type resolverMapsClient struct { - crds crdclientset.Interface - apiexts apiexts.Interface - // write and read objects to this namespace if not specified on the SqoopObjects - namespace string - syncFrequency time.Duration -} - -func (c *resolverMapsClient) Create(item *v1.ResolverMap) (*v1.ResolverMap, error) { - return c.createOrUpdateResolverMapCrd(item, crud.OperationCreate) -} - -func (c *resolverMapsClient) Update(item *v1.ResolverMap) (*v1.ResolverMap, error) { - return c.createOrUpdateResolverMapCrd(item, crud.OperationUpdate) -} - -func (c *resolverMapsClient) Delete(name string) error { - return c.crds.SqoopV1().ResolverMaps(c.namespace).Delete(name, nil) -} - -func (c *resolverMapsClient) Get(name string) (*v1.ResolverMap, error) { - crdResolverMap, err := c.crds.SqoopV1().ResolverMaps(c.namespace).Get(name, metav1.GetOptions{}) - if err != nil { - return nil, errors.Wrap(err, "failed performing get api request") - } - var returnedResolverMap v1.ResolverMap - if err := ConfigObjectFromCrd( - crdResolverMap.ObjectMeta, - crdResolverMap.Spec, - crdResolverMap.Status, - &returnedResolverMap); err != nil { - return nil, errors.Wrap(err, "converting returned crd to resolverMap") - } - return &returnedResolverMap, nil -} - -func (c *resolverMapsClient) List() ([]*v1.ResolverMap, error) { - crdList, err := c.crds.SqoopV1().ResolverMaps(c.namespace).List(metav1.ListOptions{}) - if err != nil { - return nil, errors.Wrap(err, "failed performing list api request") - } - var returnedResolverMaps []*v1.ResolverMap - for _, crdResolverMap := range crdList.Items { - var returnedResolverMap v1.ResolverMap - if err := ConfigObjectFromCrd( - crdResolverMap.ObjectMeta, - crdResolverMap.Spec, - crdResolverMap.Status, - &returnedResolverMap); err != nil { - return nil, errors.Wrap(err, "converting returned crd to resolverMap") - } - returnedResolverMaps = append(returnedResolverMaps, &returnedResolverMap) - } - return returnedResolverMaps, nil -} - -func (u *resolverMapsClient) Watch(handlers ...storage.ResolverMapEventHandler) (*storage.Watcher, error) { - lw := cache.NewListWatchFromClient(u.crds.SqoopV1().RESTClient(), crdv1.ResolverMapCRD.Plural, u.namespace, fields.Everything()) - sw := cache.NewSharedInformer(lw, new(crdv1.ResolverMap), u.syncFrequency) - for _, h := range handlers { - sw.AddEventHandler(&resolverMapEventHandler{handler: h, store: sw.GetStore()}) - } - return storage.NewWatcher(func(stop <-chan struct{}, _ chan error) { - sw.Run(stop) - }), nil -} - -func (c *resolverMapsClient) createOrUpdateResolverMapCrd(resolverMap *v1.ResolverMap, op crud.Operation) (*v1.ResolverMap, error) { - resolverMapCrd, err := ConfigObjectToCrd(c.namespace, resolverMap) - if err != nil { - return nil, errors.Wrap(err, "converting sqoop object to crd") - } - resolverMaps := c.crds.SqoopV1().ResolverMaps(resolverMapCrd.GetNamespace()) - var returnedCrd *crdv1.ResolverMap - switch op { - case crud.OperationCreate: - returnedCrd, err = resolverMaps.Create(resolverMapCrd.(*crdv1.ResolverMap)) - if err != nil { - if kuberrs.IsAlreadyExists(err) { - return nil, storage.NewAlreadyExistsErr(err) - } - return nil, errors.Wrap(err, "kubernetes create api request") - } - case crud.OperationUpdate: - // need to make sure we preserve labels - currentCrd, err := resolverMaps.Get(resolverMapCrd.GetName(), metav1.GetOptions{ResourceVersion: resolverMapCrd.GetResourceVersion()}) - if err != nil { - return nil, errors.Wrap(err, "kubernetes get api request") - } - // copy labels - resolverMapCrd.SetLabels(currentCrd.Labels) - returnedCrd, err = resolverMaps.Update(resolverMapCrd.(*crdv1.ResolverMap)) - if err != nil { - return nil, errors.Wrap(err, "kubernetes update api request") - } - } - var returnedResolverMap v1.ResolverMap - if err := ConfigObjectFromCrd( - returnedCrd.ObjectMeta, - returnedCrd.Spec, - returnedCrd.Status, - &returnedResolverMap); err != nil { - return nil, errors.Wrap(err, "converting returned crd to resolverMap") - } - return &returnedResolverMap, nil -} - -// implements the kubernetes ResourceEventHandler interface -type resolverMapEventHandler struct { - handler storage.ResolverMapEventHandler - store cache.Store -} - -func (eh *resolverMapEventHandler) getUpdatedList() []*v1.ResolverMap { - updatedList := eh.store.List() - var updatedResolverMapList []*v1.ResolverMap - for _, updated := range updatedList { - resolverMapCrd, ok := updated.(*crdv1.ResolverMap) - if !ok { - continue - } - var returnedResolverMap v1.ResolverMap - if err := ConfigObjectFromCrd( - resolverMapCrd.ObjectMeta, - resolverMapCrd.Spec, - resolverMapCrd.Status, - &returnedResolverMap); err != nil { - log.Warnf("watch event: %v", errors.Wrap(err, "converting returned crd to resolverMap")) - } - updatedResolverMapList = append(updatedResolverMapList, &returnedResolverMap) - } - return updatedResolverMapList -} - -func convertResolverMap(obj interface{}) (*v1.ResolverMap, bool) { - resolverMapCrd, ok := obj.(*crdv1.ResolverMap) - if !ok { - return nil, ok - } - var returnedResolverMap v1.ResolverMap - if err := ConfigObjectFromCrd( - resolverMapCrd.ObjectMeta, - resolverMapCrd.Spec, - resolverMapCrd.Status, - &returnedResolverMap); err != nil { - log.Warnf("watch event: %v", errors.Wrap(err, "converting returned crd to resolverMap")) - return nil, false - } - return &returnedResolverMap, true -} - -func (eh *resolverMapEventHandler) OnAdd(obj interface{}) { - resolverMap, ok := convertResolverMap(obj) - if !ok { - return - } - eh.handler.OnAdd(eh.getUpdatedList(), resolverMap) -} -func (eh *resolverMapEventHandler) OnUpdate(_, newObj interface{}) { - newResolverMap, ok := convertResolverMap(newObj) - if !ok { - return - } - eh.handler.OnUpdate(eh.getUpdatedList(), newResolverMap) -} - -func (eh *resolverMapEventHandler) OnDelete(obj interface{}) { - resolverMap, ok := convertResolverMap(obj) - if !ok { - return - } - eh.handler.OnDelete(eh.getUpdatedList(), resolverMap) -} diff --git a/pkg/storage/crd/schemas.go b/pkg/storage/crd/schemas.go deleted file mode 100644 index 903cf7a..0000000 --- a/pkg/storage/crd/schemas.go +++ /dev/null @@ -1,193 +0,0 @@ -package crd - -import ( - "time" - - "github.com/pkg/errors" - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/storage" - crdclientset "github.com/solo-io/sqoop/pkg/storage/crd/client/clientset/versioned" - crdv1 "github.com/solo-io/sqoop/pkg/storage/crd/solo.io/v1" - apiexts "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" - - "github.com/solo-io/gloo/pkg/log" - "github.com/solo-io/sqoop/pkg/storage/crud" - kuberrs "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/client-go/tools/cache" -) - -type schemasClient struct { - crds crdclientset.Interface - apiexts apiexts.Interface - // write and read objects to this namespace if not specified on the SqoopObjects - namespace string - syncFrequency time.Duration -} - -func (c *schemasClient) Create(item *v1.Schema) (*v1.Schema, error) { - return c.createOrUpdateSchemaCrd(item, crud.OperationCreate) -} - -func (c *schemasClient) Update(item *v1.Schema) (*v1.Schema, error) { - return c.createOrUpdateSchemaCrd(item, crud.OperationUpdate) -} - -func (c *schemasClient) Delete(name string) error { - return c.crds.SqoopV1().Schemas(c.namespace).Delete(name, nil) -} - -func (c *schemasClient) Get(name string) (*v1.Schema, error) { - crdSchema, err := c.crds.SqoopV1().Schemas(c.namespace).Get(name, metav1.GetOptions{}) - if err != nil { - return nil, errors.Wrap(err, "failed performing get api request") - } - var returnedSchema v1.Schema - if err := ConfigObjectFromCrd( - crdSchema.ObjectMeta, - crdSchema.Spec, - crdSchema.Status, - &returnedSchema); err != nil { - return nil, errors.Wrap(err, "converting returned crd to schema") - } - return &returnedSchema, nil -} - -func (c *schemasClient) List() ([]*v1.Schema, error) { - crdList, err := c.crds.SqoopV1().Schemas(c.namespace).List(metav1.ListOptions{}) - if err != nil { - return nil, errors.Wrap(err, "failed performing list api request") - } - var returnedSchemas []*v1.Schema - for _, crdSchema := range crdList.Items { - var returnedSchema v1.Schema - if err := ConfigObjectFromCrd( - crdSchema.ObjectMeta, - crdSchema.Spec, - crdSchema.Status, - &returnedSchema); err != nil { - return nil, errors.Wrap(err, "converting returned crd to schema") - } - returnedSchemas = append(returnedSchemas, &returnedSchema) - } - return returnedSchemas, nil -} - -func (u *schemasClient) Watch(handlers ...storage.SchemaEventHandler) (*storage.Watcher, error) { - lw := cache.NewListWatchFromClient(u.crds.SqoopV1().RESTClient(), crdv1.SchemaCRD.Plural, u.namespace, fields.Everything()) - sw := cache.NewSharedInformer(lw, new(crdv1.Schema), u.syncFrequency) - for _, h := range handlers { - sw.AddEventHandler(&schemaEventHandler{handler: h, store: sw.GetStore()}) - } - return storage.NewWatcher(func(stop <-chan struct{}, _ chan error) { - sw.Run(stop) - }), nil -} - -func (c *schemasClient) createOrUpdateSchemaCrd(schema *v1.Schema, op crud.Operation) (*v1.Schema, error) { - schemaCrd, err := ConfigObjectToCrd(c.namespace, schema) - if err != nil { - return nil, errors.Wrap(err, "converting sqoop object to crd") - } - schemas := c.crds.SqoopV1().Schemas(schemaCrd.GetNamespace()) - var returnedCrd *crdv1.Schema - switch op { - case crud.OperationCreate: - returnedCrd, err = schemas.Create(schemaCrd.(*crdv1.Schema)) - if err != nil { - if kuberrs.IsAlreadyExists(err) { - return nil, storage.NewAlreadyExistsErr(err) - } - return nil, errors.Wrap(err, "kubernetes create api request") - } - case crud.OperationUpdate: - // need to make sure we preserve labels - currentCrd, err := schemas.Get(schemaCrd.GetName(), metav1.GetOptions{ResourceVersion: schemaCrd.GetResourceVersion()}) - if err != nil { - return nil, errors.Wrap(err, "kubernetes get api request") - } - // copy labels - schemaCrd.SetLabels(currentCrd.Labels) - returnedCrd, err = schemas.Update(schemaCrd.(*crdv1.Schema)) - if err != nil { - return nil, errors.Wrap(err, "kubernetes update api request") - } - } - var returnedSchema v1.Schema - if err := ConfigObjectFromCrd( - returnedCrd.ObjectMeta, - returnedCrd.Spec, - returnedCrd.Status, - &returnedSchema); err != nil { - return nil, errors.Wrap(err, "converting returned crd to schema") - } - return &returnedSchema, nil -} - -// implements the kubernetes ResourceEventHandler interface -type schemaEventHandler struct { - handler storage.SchemaEventHandler - store cache.Store -} - -func (eh *schemaEventHandler) getUpdatedList() []*v1.Schema { - updatedList := eh.store.List() - var updatedSchemaList []*v1.Schema - for _, updated := range updatedList { - schemaCrd, ok := updated.(*crdv1.Schema) - if !ok { - continue - } - var returnedSchema v1.Schema - if err := ConfigObjectFromCrd( - schemaCrd.ObjectMeta, - schemaCrd.Spec, - schemaCrd.Status, - &returnedSchema); err != nil { - log.Warnf("watch event: %v", errors.Wrap(err, "converting returned crd to schema")) - } - updatedSchemaList = append(updatedSchemaList, &returnedSchema) - } - return updatedSchemaList -} - -func convertSchema(obj interface{}) (*v1.Schema, bool) { - schemaCrd, ok := obj.(*crdv1.Schema) - if !ok { - return nil, ok - } - var returnedSchema v1.Schema - if err := ConfigObjectFromCrd( - schemaCrd.ObjectMeta, - schemaCrd.Spec, - schemaCrd.Status, - &returnedSchema); err != nil { - log.Warnf("watch event: %v", errors.Wrap(err, "converting returned crd to schema")) - return nil, false - } - return &returnedSchema, true -} - -func (eh *schemaEventHandler) OnAdd(obj interface{}) { - schema, ok := convertSchema(obj) - if !ok { - return - } - eh.handler.OnAdd(eh.getUpdatedList(), schema) -} -func (eh *schemaEventHandler) OnUpdate(_, newObj interface{}) { - newSchema, ok := convertSchema(newObj) - if !ok { - return - } - eh.handler.OnUpdate(eh.getUpdatedList(), newSchema) -} - -func (eh *schemaEventHandler) OnDelete(obj interface{}) { - schema, ok := convertSchema(obj) - if !ok { - return - } - eh.handler.OnDelete(eh.getUpdatedList(), schema) -} diff --git a/pkg/storage/crd/solo.io/v1/doc.go b/pkg/storage/crd/solo.io/v1/doc.go deleted file mode 100644 index be18d1c..0000000 --- a/pkg/storage/crd/solo.io/v1/doc.go +++ /dev/null @@ -1,21 +0,0 @@ -/* -Copyright 2017 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// +k8s:deepcopy-gen=package - -// Package v1 is the v1 version of the API. -// +groupName=sqoop.solo.io -package v1 diff --git a/pkg/storage/crd/solo.io/v1/register.go b/pkg/storage/crd/solo.io/v1/register.go deleted file mode 100644 index 9b0d4bc..0000000 --- a/pkg/storage/crd/solo.io/v1/register.go +++ /dev/null @@ -1,75 +0,0 @@ -package v1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -const ( - GroupName = "sqoop.solo.io" - Version = "v1" -) - -var ( - SchemaCRD = crd{ - Plural: "schemas", - Group: GroupName, - Version: Version, - Kind: "Schema", - ShortName: "sm", - } - ResolverMapCRD = crd{ - Plural: "resolvermaps", - Group: GroupName, - Version: Version, - Kind: "ResolverMap", - ShortName: "rm", - } - KnownCRDs = []crd{ - SchemaCRD, - ResolverMapCRD, - } -) - -type crd struct { - Plural string - Group string - Version string - Kind string - ShortName string -} - -func (d crd) FullName() string { - return d.Plural + "." + d.Group -} - -// SchemeGroupVersion is group version used to register these objects -var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: Version} - -// Kind takes an unqualified kind and returns back a Group qualified GroupKind -func Kind(kind string) schema.GroupKind { - return SchemeGroupVersion.WithKind(kind).GroupKind() -} - -// Resource takes an unqualified resource and returns a Group qualified GroupResource -func Resource(resource string) schema.GroupResource { - return SchemeGroupVersion.WithResource(resource).GroupResource() -} - -var ( - SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) - AddToScheme = SchemeBuilder.AddToScheme -) - -// Adds the list of known types to Scheme. -func addKnownTypes(scheme *runtime.Scheme) error { - scheme.AddKnownTypes(SchemeGroupVersion, - &Schema{}, - &SchemaList{}, - &ResolverMap{}, - &ResolverMapList{}, - ) - metav1.AddToGroupVersion(scheme, SchemeGroupVersion) - return nil -} diff --git a/pkg/storage/crd/solo.io/v1/types.go b/pkg/storage/crd/solo.io/v1/types.go deleted file mode 100644 index 010eeff..0000000 --- a/pkg/storage/crd/solo.io/v1/types.go +++ /dev/null @@ -1,73 +0,0 @@ -package v1 - -import ( - "encoding/json" - - "github.com/solo-io/gloo/pkg/api/types/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// +genclient -// +genclient:noStatus -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// Schema is the generic Kubernetes API object wrapper for Gloo Schemas -type Schema struct { - metav1.TypeMeta `json:",inline"` - // +optional - metav1.ObjectMeta `json:"metadata,omitempty"` - Status *v1.Status `json:"status"` - Spec *Spec `json:"spec"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// SchemaList is the generic Kubernetes API object wrapper -type SchemaList struct { - metav1.TypeMeta `json:",inline"` - // +optional - metav1.ListMeta `json:"metadata"` - Items []Schema `json:"items"` -} - -// +genclient -// +genclient:noStatus -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// ResolverMap is the generic Kubernetes API object wrapper for Gloo ResolverMaps -type ResolverMap struct { - metav1.TypeMeta `json:",inline"` - // +optional - metav1.ObjectMeta `json:"metadata,omitempty"` - Status *v1.Status `json:"status"` - Spec *Spec `json:"spec"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// ResolverMapList is the generic Kubernetes API object wrapper -type ResolverMapList struct { - metav1.TypeMeta `json:",inline"` - // +optional - metav1.ListMeta `json:"metadata"` - metav1.Status `json:"status,omitempty"` - Items []ResolverMap `json:"items"` -} - -// spec implements deepcopy -type Spec map[string]interface{} - -func (in *Spec) DeepCopyInto(out *Spec) { - if in == nil { - out = nil - return - } - data, err := json.Marshal(in) - if err != nil { - panic(err) - } - err = json.Unmarshal(data, &out) - if err != nil { - panic(err) - } -} diff --git a/pkg/storage/crd/solo.io/v1/zz_generated.deepcopy.go b/pkg/storage/crd/solo.io/v1/zz_generated.deepcopy.go deleted file mode 100644 index 29ac458..0000000 --- a/pkg/storage/crd/solo.io/v1/zz_generated.deepcopy.go +++ /dev/null @@ -1,191 +0,0 @@ -// +build !ignore_autogenerated - -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by deepcopy-gen. DO NOT EDIT. - -package v1 - -import ( - types_v1 "github.com/solo-io/gloo/pkg/api/types/v1" - runtime "k8s.io/apimachinery/pkg/runtime" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ResolverMap) DeepCopyInto(out *ResolverMap) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - if in.Status != nil { - in, out := &in.Status, &out.Status - if *in == nil { - *out = nil - } else { - *out = new(types_v1.Status) - **out = **in - } - } - if in.Spec != nil { - in, out := &in.Spec, &out.Spec - if *in == nil { - *out = nil - } else { - x := (*in).DeepCopy() - *out = &x - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResolverMap. -func (in *ResolverMap) DeepCopy() *ResolverMap { - if in == nil { - return nil - } - out := new(ResolverMap) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ResolverMap) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ResolverMapList) DeepCopyInto(out *ResolverMapList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - in.Status.DeepCopyInto(&out.Status) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]ResolverMap, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResolverMapList. -func (in *ResolverMapList) DeepCopy() *ResolverMapList { - if in == nil { - return nil - } - out := new(ResolverMapList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ResolverMapList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Schema) DeepCopyInto(out *Schema) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - if in.Status != nil { - in, out := &in.Status, &out.Status - if *in == nil { - *out = nil - } else { - *out = new(types_v1.Status) - **out = **in - } - } - if in.Spec != nil { - in, out := &in.Spec, &out.Spec - if *in == nil { - *out = nil - } else { - x := (*in).DeepCopy() - *out = &x - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Schema. -func (in *Schema) DeepCopy() *Schema { - if in == nil { - return nil - } - out := new(Schema) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Schema) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SchemaList) DeepCopyInto(out *SchemaList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Schema, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SchemaList. -func (in *SchemaList) DeepCopy() *SchemaList { - if in == nil { - return nil - } - out := new(SchemaList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *SchemaList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Spec. -func (in Spec) DeepCopy() Spec { - if in == nil { - return nil - } - out := new(Spec) - in.DeepCopyInto(out) - return *out -} diff --git a/pkg/storage/crud/crud.go b/pkg/storage/crud/crud.go deleted file mode 100644 index b03429a..0000000 --- a/pkg/storage/crud/crud.go +++ /dev/null @@ -1,8 +0,0 @@ -package crud - -type Operation int - -const ( - OperationCreate Operation = iota - OperationUpdate = iota -) diff --git a/pkg/storage/errors.go b/pkg/storage/errors.go deleted file mode 100644 index fcde704..0000000 --- a/pkg/storage/errors.go +++ /dev/null @@ -1,25 +0,0 @@ -package storage - -import "fmt" - -// a special kind of error returned by "create" funcs -// can be used by callers to tell if they can ignore create errors -type alreadyExistsErr struct { - err error -} - -func (err *alreadyExistsErr) Error() string { - return fmt.Sprintf("already exists: %v", err.err.Error()) -} - -func NewAlreadyExistsErr(err error) *alreadyExistsErr { - return &alreadyExistsErr{err: err} -} - -func IsAlreadyExists(err error) bool { - switch err.(type) { - case *alreadyExistsErr: - return true - } - return false -} diff --git a/pkg/storage/file/client_template.go.tmpl b/pkg/storage/file/client_template.go.tmpl deleted file mode 100644 index 5236760..0000000 --- a/pkg/storage/file/client_template.go.tmpl +++ /dev/null @@ -1,248 +0,0 @@ -package file - -import ( - "io/ioutil" - "os" - "path/filepath" - "strings" - - "github.com/gogo/protobuf/proto" - "github.com/pkg/errors" - "github.com/radovskyb/watcher" - - "time" - - gloov1 "github.com/solo-io/gloo/pkg/api/types/v1" - "github.com/solo-io/gloo/pkg/log" - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/storage" -) - -// TODO: evaluate efficiency of LSing a whole dir on every op -// so far this is preferable to caring what files are named -type {{ .LowercasePluralName }}Client struct { - dir string - syncFrequency time.Duration -} - -func (c *{{ .LowercasePluralName }}Client) Create(item *v1.{{ .UppercaseName }}) (*v1.{{ .UppercaseName }}, error) { - if item.Name == "" { - return nil, errors.Errorf("name required") - } - // set resourceversion on clone - {{ .LowercaseName }}Clone, ok := proto.Clone(item).(*v1.{{ .UppercaseName }}) - if !ok { - return nil, errors.New("internal error: output of proto.Clone was not expected type") - } - if {{ .LowercaseName }}Clone.Metadata == nil { - {{ .LowercaseName }}Clone.Metadata = &gloov1.Metadata{} - } - {{ .LowercaseName }}Clone.Metadata.ResourceVersion = newOrIncrementResourceVer({{ .LowercaseName }}Clone.Metadata.ResourceVersion) - {{ .LowercaseName }}Files, err := c.pathsTo{{ .UppercasePluralName }}() - if err != nil { - return nil, errors.Wrap(err, "failed to read {{ .LowercaseName }} dir") - } - // error if exists already - for file, existingUps := range {{ .LowercaseName }}Files { - if existingUps.Name == item.Name { - return nil, storage.NewAlreadyExistsErr(errors.Errorf("{{ .LowercaseName }} %v already defined in %s", item.Name, file)) - } - } - filename := filepath.Join(c.dir, item.Name+".yml") - err = WriteToFile(filename, {{ .LowercaseName }}Clone) - if err != nil { - return nil, errors.Wrap(err, "failed creating file") - } - return {{ .LowercaseName }}Clone, nil -} - -func (c *{{ .LowercasePluralName }}Client) Update(item *v1.{{ .UppercaseName }}) (*v1.{{ .UppercaseName }}, error) { - if item.Name == "" { - return nil, errors.Errorf("name required") - } - if item.Metadata == nil || item.Metadata.ResourceVersion == "" { - return nil, errors.New("resource version must be set for update operations") - } - {{ .LowercaseName }}Files, err := c.pathsTo{{ .UppercasePluralName }}() - if err != nil { - return nil, errors.Wrap(err, "failed to read {{ .LowercaseName }} dir") - } - // error if exists already - for file, existingUps := range {{ .LowercaseName }}Files { - if existingUps.Name != item.Name { - continue - } - if existingUps.Metadata != nil && lessThan(item.Metadata.ResourceVersion, existingUps.Metadata.ResourceVersion) { - return nil, errors.Errorf("resource version outdated for %v", item.Name) - } - {{ .LowercaseName }}Clone, ok := proto.Clone(item).(*v1.{{ .UppercaseName }}) - if !ok { - return nil, errors.New("internal error: output of proto.Clone was not expected type") - } - {{ .LowercaseName }}Clone.Metadata.ResourceVersion = newOrIncrementResourceVer({{ .LowercaseName }}Clone.Metadata.ResourceVersion) - - err = WriteToFile(file, {{ .LowercaseName }}Clone) - if err != nil { - return nil, errors.Wrap(err, "failed creating file") - } - - return {{ .LowercaseName }}Clone, nil - } - return nil, errors.Errorf("{{ .LowercaseName }} %v not found", item.Name) -} - -func (c *{{ .LowercasePluralName }}Client) Delete(name string) error { - {{ .LowercaseName }}Files, err := c.pathsTo{{ .UppercasePluralName }}() - if err != nil { - return errors.Wrap(err, "failed to read {{ .LowercaseName }} dir") - } - // error if exists already - for file, existingUps := range {{ .LowercaseName }}Files { - if existingUps.Name == name { - return os.Remove(file) - } - } - return errors.Errorf("file not found for {{ .LowercaseName }} %v", name) -} - -func (c *{{ .LowercasePluralName }}Client) Get(name string) (*v1.{{ .UppercaseName }}, error) { - {{ .LowercaseName }}Files, err := c.pathsTo{{ .UppercasePluralName }}() - if err != nil { - return nil, errors.Wrap(err, "failed to read {{ .LowercaseName }} dir") - } - // error if exists already - for _, existingUps := range {{ .LowercaseName }}Files { - if existingUps.Name == name { - return existingUps, nil - } - } - return nil, errors.Errorf("file not found for {{ .LowercaseName }} %v", name) -} - -func (c *{{ .LowercasePluralName }}Client) List() ([]*v1.{{ .UppercaseName }}, error) { - {{ .LowercaseName }}Paths, err := c.pathsTo{{ .UppercasePluralName }}() - if err != nil { - return nil, err - } - var {{ .LowercasePluralName }} []*v1.{{ .UppercaseName }} - for _, up := range {{ .LowercaseName }}Paths { - {{ .LowercasePluralName }} = append({{ .LowercasePluralName }}, up) - } - return {{ .LowercasePluralName }}, nil -} - -func (c *{{ .LowercasePluralName }}Client) pathsTo{{ .UppercasePluralName }}() (map[string]*v1.{{ .UppercaseName }}, error) { - files, err := ioutil.ReadDir(c.dir) - if err != nil { - return nil, errors.Wrap(err, "could not read dir") - } - {{ .LowercasePluralName }} := make(map[string]*v1.{{ .UppercaseName }}) - for _, f := range files { - path := filepath.Join(c.dir, f.Name()) - if !strings.HasSuffix(path, ".yml") && !strings.HasSuffix(path, ".yaml") { - continue - } - - {{ .LowercaseName }}, err := pathTo{{ .UppercaseName }}(path) - if err != nil { - return nil, errors.Wrap(err, "unable to parse .yml file as {{ .LowercaseName }}") - } - - {{ .LowercasePluralName }}[path] = {{ .LowercaseName }} - } - return {{ .LowercasePluralName }}, nil -} - -func pathTo{{ .UppercaseName }}(path string) (*v1.{{ .UppercaseName }}, error) { - var {{ .LowercaseName }} v1.{{ .UppercaseName }} - err := ReadFileInto(path, &{{ .LowercaseName }}) - if err != nil { - return nil, err - } - if {{ .LowercaseName }}.Metadata == nil { - {{ .LowercaseName }}.Metadata = &gloov1.Metadata{} - } - if {{ .LowercaseName }}.Metadata.ResourceVersion == "" { - {{ .LowercaseName }}.Metadata.ResourceVersion = "1" - } - return &{{ .LowercaseName }}, nil -} - -func (u *{{ .LowercasePluralName }}Client) Watch(handlers ...storage.{{ .UppercaseName }}EventHandler) (*storage.Watcher, error) { - w := watcher.New() - w.SetMaxEvents(0) - w.FilterOps(watcher.Create, watcher.Write, watcher.Remove) - if err := w.AddRecursive(u.dir); err != nil { - return nil, errors.Wrapf(err, "failed to add directory %v", u.dir) - } - - return storage.NewWatcher(func(stop <-chan struct{}, errs chan error) { - go func() { - if err := w.Start(u.syncFrequency); err != nil { - errs <- err - } - }() - // start the watch with an "initial read" event - current, err := u.List() - if err != nil { - errs <- err - return - } - for _, h := range handlers { - h.OnAdd(current, nil) - } - for { - select { - case event := <-w.Event: - if err := u.onEvent(event, handlers...); err != nil { - log.Warnf("event handle error in file-based config storage client: %v", err) - } - case err := <-w.Error: - log.Warnf("watcher error in file-based config storage client: %v", err) - return - case err := <-errs: - log.Warnf("failed to start file watcher: %v", err) - return - case <-stop: - w.Close() - return - } - } - }), nil -} - -func (u *{{ .LowercasePluralName }}Client) onEvent(event watcher.Event, handlers ...storage.{{ .UppercaseName }}EventHandler) error { - log.Debugf("file event: %v [%v]", event.Path, event.Op) - current, err := u.List() - if err != nil { - return err - } - if event.IsDir() { - return nil - } - switch event.Op { - case watcher.Create: - for _, h := range handlers { - created, err := pathTo{{ .UppercaseName }}(event.Path) - if err != nil { - return err - } - h.OnAdd(current, created) - } - case watcher.Write: - for _, h := range handlers { - updated, err := pathTo{{ .UppercaseName }}(event.Path) - if err != nil { - return err - } - h.OnUpdate(current, updated) - } - case watcher.Remove: - for _, h := range handlers { - // can't read the deleted object - // callers beware - h.OnDelete(current, nil) - } - } - return nil -} diff --git a/pkg/storage/file/directory.go b/pkg/storage/file/directory.go deleted file mode 100644 index c4301a3..0000000 --- a/pkg/storage/file/directory.go +++ /dev/null @@ -1,3 +0,0 @@ -package file - -const GlooDefaultDirectory = "_gloo_config" diff --git a/pkg/storage/file/file_io.go b/pkg/storage/file/file_io.go deleted file mode 100644 index e0652db..0000000 --- a/pkg/storage/file/file_io.go +++ /dev/null @@ -1,31 +0,0 @@ -package file - -import ( - "io/ioutil" - - "github.com/ghodss/yaml" - "github.com/gogo/protobuf/proto" - "github.com/pkg/errors" - "github.com/solo-io/gloo/pkg/protoutil" -) - -func WriteToFile(filename string, pb proto.Message) error { - jsn, err := protoutil.Marshal(pb) - data, err := yaml.JSONToYAML(jsn) - if err != nil { - return err - } - return ioutil.WriteFile(filename, data, 0644) -} - -func ReadFileInto(filename string, v proto.Message) error { - data, err := ioutil.ReadFile(filename) - if err != nil { - return errors.Errorf("error reading file: %v", err) - } - jsn, err := yaml.YAMLToJSON(data) - if err != nil { - return err - } - return protoutil.Unmarshal(jsn, v) -} diff --git a/pkg/storage/file/file_storage_client.go b/pkg/storage/file/file_storage_client.go deleted file mode 100644 index 85b0a56..0000000 --- a/pkg/storage/file/file_storage_client.go +++ /dev/null @@ -1,65 +0,0 @@ -package file - -import ( - "os" - "path/filepath" - "time" - - "github.com/solo-io/sqoop/pkg/storage" -) - -//go:generate go run ${GOPATH}/src/github.com/solo-io/sqoop/pkg/storage/generate/generate_clients.go -f ${GOPATH}/src/github.com/solo-io/sqoop/pkg/storage/file/client_template.go.tmpl -o ${GOPATH}/src/github.com/solo-io/sqoop/pkg/storage/file/ - -type Client struct { - v1 *v1client -} - -const schemasDir = "schemas" -const resolverMapsDir = "resolver_maps" - -func NewStorage(dir string, syncFrequency time.Duration) (storage.Interface, error) { - if dir == "" { - dir = GlooDefaultDirectory - } - return &Client{ - v1: &v1client{ - schemas: &schemasClient{ - dir: filepath.Join(dir, schemasDir), - syncFrequency: syncFrequency, - }, - resolverMaps: &resolverMapsClient{ - dir: filepath.Join(dir, resolverMapsDir), - syncFrequency: syncFrequency, - }, - }, - }, nil -} - -func (c *Client) V1() storage.V1 { - return c.v1 -} - -type v1client struct { - schemas *schemasClient - resolverMaps *resolverMapsClient -} - -func (c *v1client) Register() error { - err := os.MkdirAll(c.schemas.dir, 0755) - if err != nil && err != os.ErrExist { - return err - } - err = os.MkdirAll(c.resolverMaps.dir, 0755) - if err != nil && err != os.ErrExist { - return err - } - return nil -} - -func (c *v1client) Schemas() storage.Schemas { - return c.schemas -} - -func (c *v1client) ResolverMaps() storage.ResolverMaps { - return c.resolverMaps -} diff --git a/pkg/storage/file/file_storage_client_test.go b/pkg/storage/file/file_storage_client_test.go deleted file mode 100644 index 60aec6b..0000000 --- a/pkg/storage/file/file_storage_client_test.go +++ /dev/null @@ -1,191 +0,0 @@ -package file_test - -import ( - "io/ioutil" - "os" - - "time" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - gloov1 "github.com/solo-io/gloo/pkg/api/types/v1" - "github.com/solo-io/gloo/pkg/log" - . "github.com/solo-io/gloo/test/helpers" - "github.com/solo-io/sqoop/pkg/api/types/v1" - . "github.com/solo-io/sqoop/pkg/storage/file" -) - -var _ = Describe("CrdStorageClient", func() { - var ( - dir string - err error - resync = time.Second - ) - BeforeEach(func() { - dir, err = ioutil.TempDir("", "filecachetest") - Must(err) - }) - AfterEach(func() { - log.Debugf("removing " + dir) - os.RemoveAll(dir) - }) - Describe("New", func() { - It("creates a new client without error", func() { - _, err = NewStorage(dir, resync) - Expect(err).NotTo(HaveOccurred()) - }) - }) - Describe("Create", func() { - It("creates a file from the item", func() { - client, err := NewStorage(dir, resync) - Expect(err).NotTo(HaveOccurred()) - err = client.V1().Register() - Expect(err).NotTo(HaveOccurred()) - schema := NewTestSchema1() - createdSchema, err := client.V1().Schemas().Create(schema) - Expect(err).NotTo(HaveOccurred()) - schema.Metadata = createdSchema.GetMetadata() - Expect(schema).To(Equal(createdSchema)) - }) - }) - Describe("Create2Update", func() { - It("creates and updates", func() { - client, err := NewStorage(dir, resync) - Expect(err).NotTo(HaveOccurred()) - err = client.V1().Register() - Expect(err).NotTo(HaveOccurred()) - schema := NewTestSchema1() - schema, err = client.V1().Schemas().Create(schema) - schema2 := NewTestSchema2() - schema2, err = client.V1().Schemas().Create(schema2) - Expect(err).NotTo(HaveOccurred()) - - _, err = client.V1().Schemas().Update(schema2) - Expect(err).NotTo(HaveOccurred()) - - created1, err := client.V1().Schemas().Get(schema.Name) - Expect(err).NotTo(HaveOccurred()) - schema.Metadata = created1.Metadata - Expect(created1).To(Equal(schema)) - - created2, err := client.V1().Schemas().Get(schema2.Name) - Expect(err).NotTo(HaveOccurred()) - schema2.Metadata = created2.Metadata - Expect(created2).To(Equal(schema2)) - - }) - }) - Describe("Create2Update resolverMap", func() { - It("creates and updates", func() { - client, err := NewStorage(dir, resync) - Expect(err).NotTo(HaveOccurred()) - err = client.V1().Register() - Expect(err).NotTo(HaveOccurred()) - resolverMap := NewTestResolverMap("v1") - resolverMap, err = client.V1().ResolverMaps().Create(resolverMap) - resolverMap2 := NewTestResolverMap("v2") - resolverMap2, err = client.V1().ResolverMaps().Create(resolverMap2) - Expect(err).NotTo(HaveOccurred()) - - _, err = client.V1().ResolverMaps().Update(resolverMap) - Expect(err).NotTo(HaveOccurred()) - - created1, err := client.V1().ResolverMaps().Get(resolverMap.Name) - Expect(err).NotTo(HaveOccurred()) - resolverMap.Metadata = created1.Metadata - Expect(created1).To(Equal(resolverMap)) - - created2, err := client.V1().ResolverMaps().Get(resolverMap2.Name) - Expect(err).NotTo(HaveOccurred()) - resolverMap2.Metadata = created2.Metadata - Expect(created2).To(Equal(resolverMap2)) - }) - }) - - Describe("Get", func() { - It("gets a file from the name", func() { - client, err := NewStorage(dir, resync) - Expect(err).NotTo(HaveOccurred()) - err = client.V1().Register() - Expect(err).NotTo(HaveOccurred()) - schema := NewTestSchema1() - _, err = client.V1().Schemas().Create(schema) - Expect(err).NotTo(HaveOccurred()) - created, err := client.V1().Schemas().Get(schema.Name) - Expect(err).NotTo(HaveOccurred()) - schema.Metadata = created.Metadata - Expect(created).To(Equal(schema)) - }) - }) - Describe("Update", func() { - It("updates a file from the item", func() { - client, err := NewStorage(dir, resync) - Expect(err).NotTo(HaveOccurred()) - err = client.V1().Register() - Expect(err).NotTo(HaveOccurred()) - schema := NewTestSchema1() - created, err := client.V1().Schemas().Create(schema) - Expect(err).NotTo(HaveOccurred()) - schema.InlineSchema = "something-else" - _, err = client.V1().Schemas().Update(schema) - // need to set resource ver - Expect(err).To(HaveOccurred()) - schema.Metadata = created.GetMetadata() - updated, err := client.V1().Schemas().Update(schema) - Expect(err).NotTo(HaveOccurred()) - schema.Metadata = updated.GetMetadata() - Expect(updated).To(Equal(schema)) - }) - }) - Describe("Delete", func() { - It("deletes a file from the name", func() { - client, err := NewStorage(dir, resync) - Expect(err).NotTo(HaveOccurred()) - err = client.V1().Register() - Expect(err).NotTo(HaveOccurred()) - schema := NewTestSchema1() - _, err = client.V1().Schemas().Create(schema) - Expect(err).NotTo(HaveOccurred()) - err = client.V1().Schemas().Delete(schema.Name) - Expect(err).NotTo(HaveOccurred()) - _, err = client.V1().Schemas().Get(schema.Name) - Expect(err).To(HaveOccurred()) - }) - }) -}) - -func NewTestSchema1() *v1.Schema { - return &v1.Schema{ - Name: "schema1", - ResolverMap: "resolvers", - InlineSchema: "SOMETHING", - Metadata: &gloov1.Metadata{ - Annotations: map[string]string{ - "foo": "bar", - }, - }, - } -} - -func NewTestSchema2() *v1.Schema { - return &v1.Schema{ - Name: "schema2", - ResolverMap: "resolvers", - InlineSchema: "SOMETHINGELSE", - Metadata: &gloov1.Metadata{ - Annotations: map[string]string{ - "foo": "bar", - }, - }, - } -} -func NewTestResolverMap(name string) *v1.ResolverMap { - return &v1.ResolverMap{ - Name: name, - Metadata: &gloov1.Metadata{ - Annotations: map[string]string{ - "foo": "bar", - }, - }, - } -} diff --git a/pkg/storage/file/file_suite_test.go b/pkg/storage/file/file_suite_test.go deleted file mode 100644 index b3b9690..0000000 --- a/pkg/storage/file/file_suite_test.go +++ /dev/null @@ -1,15 +0,0 @@ -package file - -import ( - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "github.com/solo-io/gloo/pkg/log" -) - -func TestFile(t *testing.T) { - RegisterFailHandler(Fail) - log.DefaultOut = GinkgoWriter - RunSpecs(t, "File Suite") -} diff --git a/pkg/storage/file/resolver_maps.go b/pkg/storage/file/resolver_maps.go deleted file mode 100644 index 08ceedc..0000000 --- a/pkg/storage/file/resolver_maps.go +++ /dev/null @@ -1,248 +0,0 @@ -package file - -import ( - "io/ioutil" - "os" - "path/filepath" - "strings" - - "github.com/gogo/protobuf/proto" - "github.com/pkg/errors" - "github.com/radovskyb/watcher" - - "time" - - gloov1 "github.com/solo-io/gloo/pkg/api/types/v1" - "github.com/solo-io/gloo/pkg/log" - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/storage" -) - -// TODO: evaluate efficiency of LSing a whole dir on every op -// so far this is preferable to caring what files are named -type resolverMapsClient struct { - dir string - syncFrequency time.Duration -} - -func (c *resolverMapsClient) Create(item *v1.ResolverMap) (*v1.ResolverMap, error) { - if item.Name == "" { - return nil, errors.Errorf("name required") - } - // set resourceversion on clone - resolverMapClone, ok := proto.Clone(item).(*v1.ResolverMap) - if !ok { - return nil, errors.New("internal error: output of proto.Clone was not expected type") - } - if resolverMapClone.Metadata == nil { - resolverMapClone.Metadata = &gloov1.Metadata{} - } - resolverMapClone.Metadata.ResourceVersion = newOrIncrementResourceVer(resolverMapClone.Metadata.ResourceVersion) - resolverMapFiles, err := c.pathsToResolverMaps() - if err != nil { - return nil, errors.Wrap(err, "failed to read resolverMap dir") - } - // error if exists already - for file, existingUps := range resolverMapFiles { - if existingUps.Name == item.Name { - return nil, storage.NewAlreadyExistsErr(errors.Errorf("resolverMap %v already defined in %s", item.Name, file)) - } - } - filename := filepath.Join(c.dir, item.Name+".yml") - err = WriteToFile(filename, resolverMapClone) - if err != nil { - return nil, errors.Wrap(err, "failed creating file") - } - return resolverMapClone, nil -} - -func (c *resolverMapsClient) Update(item *v1.ResolverMap) (*v1.ResolverMap, error) { - if item.Name == "" { - return nil, errors.Errorf("name required") - } - if item.Metadata == nil || item.Metadata.ResourceVersion == "" { - return nil, errors.New("resource version must be set for update operations") - } - resolverMapFiles, err := c.pathsToResolverMaps() - if err != nil { - return nil, errors.Wrap(err, "failed to read resolverMap dir") - } - // error if exists already - for file, existingUps := range resolverMapFiles { - if existingUps.Name != item.Name { - continue - } - if existingUps.Metadata != nil && lessThan(item.Metadata.ResourceVersion, existingUps.Metadata.ResourceVersion) { - return nil, errors.Errorf("resource version outdated for %v", item.Name) - } - resolverMapClone, ok := proto.Clone(item).(*v1.ResolverMap) - if !ok { - return nil, errors.New("internal error: output of proto.Clone was not expected type") - } - resolverMapClone.Metadata.ResourceVersion = newOrIncrementResourceVer(resolverMapClone.Metadata.ResourceVersion) - - err = WriteToFile(file, resolverMapClone) - if err != nil { - return nil, errors.Wrap(err, "failed creating file") - } - - return resolverMapClone, nil - } - return nil, errors.Errorf("resolverMap %v not found", item.Name) -} - -func (c *resolverMapsClient) Delete(name string) error { - resolverMapFiles, err := c.pathsToResolverMaps() - if err != nil { - return errors.Wrap(err, "failed to read resolverMap dir") - } - // error if exists already - for file, existingUps := range resolverMapFiles { - if existingUps.Name == name { - return os.Remove(file) - } - } - return errors.Errorf("file not found for resolverMap %v", name) -} - -func (c *resolverMapsClient) Get(name string) (*v1.ResolverMap, error) { - resolverMapFiles, err := c.pathsToResolverMaps() - if err != nil { - return nil, errors.Wrap(err, "failed to read resolverMap dir") - } - // error if exists already - for _, existingUps := range resolverMapFiles { - if existingUps.Name == name { - return existingUps, nil - } - } - return nil, errors.Errorf("file not found for resolverMap %v", name) -} - -func (c *resolverMapsClient) List() ([]*v1.ResolverMap, error) { - resolverMapPaths, err := c.pathsToResolverMaps() - if err != nil { - return nil, err - } - var resolverMaps []*v1.ResolverMap - for _, up := range resolverMapPaths { - resolverMaps = append(resolverMaps, up) - } - return resolverMaps, nil -} - -func (c *resolverMapsClient) pathsToResolverMaps() (map[string]*v1.ResolverMap, error) { - files, err := ioutil.ReadDir(c.dir) - if err != nil { - return nil, errors.Wrap(err, "could not read dir") - } - resolverMaps := make(map[string]*v1.ResolverMap) - for _, f := range files { - path := filepath.Join(c.dir, f.Name()) - if !strings.HasSuffix(path, ".yml") && !strings.HasSuffix(path, ".yaml") { - continue - } - - resolverMap, err := pathToResolverMap(path) - if err != nil { - return nil, errors.Wrap(err, "unable to parse .yml file as resolverMap") - } - - resolverMaps[path] = resolverMap - } - return resolverMaps, nil -} - -func pathToResolverMap(path string) (*v1.ResolverMap, error) { - var resolverMap v1.ResolverMap - err := ReadFileInto(path, &resolverMap) - if err != nil { - return nil, err - } - if resolverMap.Metadata == nil { - resolverMap.Metadata = &gloov1.Metadata{} - } - if resolverMap.Metadata.ResourceVersion == "" { - resolverMap.Metadata.ResourceVersion = "1" - } - return &resolverMap, nil -} - -func (u *resolverMapsClient) Watch(handlers ...storage.ResolverMapEventHandler) (*storage.Watcher, error) { - w := watcher.New() - w.SetMaxEvents(0) - w.FilterOps(watcher.Create, watcher.Write, watcher.Remove) - if err := w.AddRecursive(u.dir); err != nil { - return nil, errors.Wrapf(err, "failed to add directory %v", u.dir) - } - - return storage.NewWatcher(func(stop <-chan struct{}, errs chan error) { - go func() { - if err := w.Start(u.syncFrequency); err != nil { - errs <- err - } - }() - // start the watch with an "initial read" event - current, err := u.List() - if err != nil { - errs <- err - return - } - for _, h := range handlers { - h.OnAdd(current, nil) - } - for { - select { - case event := <-w.Event: - if err := u.onEvent(event, handlers...); err != nil { - log.Warnf("event handle error in file-based config storage client: %v", err) - } - case err := <-w.Error: - log.Warnf("watcher error in file-based config storage client: %v", err) - return - case err := <-errs: - log.Warnf("failed to start file watcher: %v", err) - return - case <-stop: - w.Close() - return - } - } - }), nil -} - -func (u *resolverMapsClient) onEvent(event watcher.Event, handlers ...storage.ResolverMapEventHandler) error { - log.Debugf("file event: %v [%v]", event.Path, event.Op) - current, err := u.List() - if err != nil { - return err - } - if event.IsDir() { - return nil - } - switch event.Op { - case watcher.Create: - for _, h := range handlers { - created, err := pathToResolverMap(event.Path) - if err != nil { - return err - } - h.OnAdd(current, created) - } - case watcher.Write: - for _, h := range handlers { - updated, err := pathToResolverMap(event.Path) - if err != nil { - return err - } - h.OnUpdate(current, updated) - } - case watcher.Remove: - for _, h := range handlers { - // can't read the deleted object - // callers beware - h.OnDelete(current, nil) - } - } - return nil -} diff --git a/pkg/storage/file/resource_ver.go b/pkg/storage/file/resource_ver.go deleted file mode 100644 index 08e48f4..0000000 --- a/pkg/storage/file/resource_ver.go +++ /dev/null @@ -1,18 +0,0 @@ -package file - -import ( - "fmt" - "strconv" -) - -func newOrIncrementResourceVer(resourceVersion string) string { - curr, err := strconv.Atoi(resourceVersion) - if err != nil { - curr = 1 - } - return fmt.Sprintf("%v", curr) -} - -func lessThan(rv1, rv2 string) bool { - return newOrIncrementResourceVer(rv1) < newOrIncrementResourceVer(rv2) -} diff --git a/pkg/storage/file/schemas.go b/pkg/storage/file/schemas.go deleted file mode 100644 index 562140d..0000000 --- a/pkg/storage/file/schemas.go +++ /dev/null @@ -1,248 +0,0 @@ -package file - -import ( - "io/ioutil" - "os" - "path/filepath" - "strings" - - "github.com/gogo/protobuf/proto" - "github.com/pkg/errors" - "github.com/radovskyb/watcher" - - "time" - - gloov1 "github.com/solo-io/gloo/pkg/api/types/v1" - "github.com/solo-io/gloo/pkg/log" - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/storage" -) - -// TODO: evaluate efficiency of LSing a whole dir on every op -// so far this is preferable to caring what files are named -type schemasClient struct { - dir string - syncFrequency time.Duration -} - -func (c *schemasClient) Create(item *v1.Schema) (*v1.Schema, error) { - if item.Name == "" { - return nil, errors.Errorf("name required") - } - // set resourceversion on clone - schemaClone, ok := proto.Clone(item).(*v1.Schema) - if !ok { - return nil, errors.New("internal error: output of proto.Clone was not expected type") - } - if schemaClone.Metadata == nil { - schemaClone.Metadata = &gloov1.Metadata{} - } - schemaClone.Metadata.ResourceVersion = newOrIncrementResourceVer(schemaClone.Metadata.ResourceVersion) - schemaFiles, err := c.pathsToSchemas() - if err != nil { - return nil, errors.Wrap(err, "failed to read schema dir") - } - // error if exists already - for file, existingUps := range schemaFiles { - if existingUps.Name == item.Name { - return nil, storage.NewAlreadyExistsErr(errors.Errorf("schema %v already defined in %s", item.Name, file)) - } - } - filename := filepath.Join(c.dir, item.Name+".yml") - err = WriteToFile(filename, schemaClone) - if err != nil { - return nil, errors.Wrap(err, "failed creating file") - } - return schemaClone, nil -} - -func (c *schemasClient) Update(item *v1.Schema) (*v1.Schema, error) { - if item.Name == "" { - return nil, errors.Errorf("name required") - } - if item.Metadata == nil || item.Metadata.ResourceVersion == "" { - return nil, errors.New("resource version must be set for update operations") - } - schemaFiles, err := c.pathsToSchemas() - if err != nil { - return nil, errors.Wrap(err, "failed to read schema dir") - } - // error if exists already - for file, existingUps := range schemaFiles { - if existingUps.Name != item.Name { - continue - } - if existingUps.Metadata != nil && lessThan(item.Metadata.ResourceVersion, existingUps.Metadata.ResourceVersion) { - return nil, errors.Errorf("resource version outdated for %v", item.Name) - } - schemaClone, ok := proto.Clone(item).(*v1.Schema) - if !ok { - return nil, errors.New("internal error: output of proto.Clone was not expected type") - } - schemaClone.Metadata.ResourceVersion = newOrIncrementResourceVer(schemaClone.Metadata.ResourceVersion) - - err = WriteToFile(file, schemaClone) - if err != nil { - return nil, errors.Wrap(err, "failed creating file") - } - - return schemaClone, nil - } - return nil, errors.Errorf("schema %v not found", item.Name) -} - -func (c *schemasClient) Delete(name string) error { - schemaFiles, err := c.pathsToSchemas() - if err != nil { - return errors.Wrap(err, "failed to read schema dir") - } - // error if exists already - for file, existingUps := range schemaFiles { - if existingUps.Name == name { - return os.Remove(file) - } - } - return errors.Errorf("file not found for schema %v", name) -} - -func (c *schemasClient) Get(name string) (*v1.Schema, error) { - schemaFiles, err := c.pathsToSchemas() - if err != nil { - return nil, errors.Wrap(err, "failed to read schema dir") - } - // error if exists already - for _, existingUps := range schemaFiles { - if existingUps.Name == name { - return existingUps, nil - } - } - return nil, errors.Errorf("file not found for schema %v", name) -} - -func (c *schemasClient) List() ([]*v1.Schema, error) { - schemaPaths, err := c.pathsToSchemas() - if err != nil { - return nil, err - } - var schemas []*v1.Schema - for _, up := range schemaPaths { - schemas = append(schemas, up) - } - return schemas, nil -} - -func (c *schemasClient) pathsToSchemas() (map[string]*v1.Schema, error) { - files, err := ioutil.ReadDir(c.dir) - if err != nil { - return nil, errors.Wrap(err, "could not read dir") - } - schemas := make(map[string]*v1.Schema) - for _, f := range files { - path := filepath.Join(c.dir, f.Name()) - if !strings.HasSuffix(path, ".yml") && !strings.HasSuffix(path, ".yaml") { - continue - } - - schema, err := pathToSchema(path) - if err != nil { - return nil, errors.Wrap(err, "unable to parse .yml file as schema") - } - - schemas[path] = schema - } - return schemas, nil -} - -func pathToSchema(path string) (*v1.Schema, error) { - var schema v1.Schema - err := ReadFileInto(path, &schema) - if err != nil { - return nil, err - } - if schema.Metadata == nil { - schema.Metadata = &gloov1.Metadata{} - } - if schema.Metadata.ResourceVersion == "" { - schema.Metadata.ResourceVersion = "1" - } - return &schema, nil -} - -func (u *schemasClient) Watch(handlers ...storage.SchemaEventHandler) (*storage.Watcher, error) { - w := watcher.New() - w.SetMaxEvents(0) - w.FilterOps(watcher.Create, watcher.Write, watcher.Remove) - if err := w.AddRecursive(u.dir); err != nil { - return nil, errors.Wrapf(err, "failed to add directory %v", u.dir) - } - - return storage.NewWatcher(func(stop <-chan struct{}, errs chan error) { - go func() { - if err := w.Start(u.syncFrequency); err != nil { - errs <- err - } - }() - // start the watch with an "initial read" event - current, err := u.List() - if err != nil { - errs <- err - return - } - for _, h := range handlers { - h.OnAdd(current, nil) - } - for { - select { - case event := <-w.Event: - if err := u.onEvent(event, handlers...); err != nil { - log.Warnf("event handle error in file-based config storage client: %v", err) - } - case err := <-w.Error: - log.Warnf("watcher error in file-based config storage client: %v", err) - return - case err := <-errs: - log.Warnf("failed to start file watcher: %v", err) - return - case <-stop: - w.Close() - return - } - } - }), nil -} - -func (u *schemasClient) onEvent(event watcher.Event, handlers ...storage.SchemaEventHandler) error { - log.Debugf("file event: %v [%v]", event.Path, event.Op) - current, err := u.List() - if err != nil { - return err - } - if event.IsDir() { - return nil - } - switch event.Op { - case watcher.Create: - for _, h := range handlers { - created, err := pathToSchema(event.Path) - if err != nil { - return err - } - h.OnAdd(current, created) - } - case watcher.Write: - for _, h := range handlers { - updated, err := pathToSchema(event.Path) - if err != nil { - return err - } - h.OnUpdate(current, updated) - } - case watcher.Remove: - for _, h := range handlers { - // can't read the deleted object - // callers beware - h.OnDelete(current, nil) - } - } - return nil -} diff --git a/pkg/storage/generate/generate_clients.go b/pkg/storage/generate/generate_clients.go deleted file mode 100644 index c004e2c..0000000 --- a/pkg/storage/generate/generate_clients.go +++ /dev/null @@ -1,71 +0,0 @@ -package main - -import ( - "bytes" - "flag" - "io/ioutil" - "path/filepath" - "text/template" - - "github.com/pkg/errors" - "github.com/solo-io/gloo/pkg/log" -) - -type clientType struct { - FilenamePrefix string - LowercaseName string - LowercasePluralName string - UppercaseName string - UppercasePluralName string -} - -var clients = []clientType{ - { - FilenamePrefix: "schemas", - LowercaseName: "schema", - LowercasePluralName: "schemas", - UppercaseName: "Schema", - UppercasePluralName: "Schemas", - }, - { - FilenamePrefix: "resolver_maps", - LowercaseName: "resolverMap", - LowercasePluralName: "resolverMaps", - UppercaseName: "ResolverMap", - UppercasePluralName: "ResolverMaps", - }, -} - -func main() { - inputFile := flag.String("f", "", "input client template") - outputDirectory := flag.String("o", "", "output directory for client files") - flag.Parse() - if *inputFile == "" || *outputDirectory == "" { - log.Fatalf("must specify -f and -o") - } - if err := writeClientTemplates(*inputFile, *outputDirectory); err != nil { - log.Fatalf("failed generating client templates: %s", err.Error()) - } - log.Printf("success") -} - -func writeClientTemplates(inputFile, outputDir string) error { - fileName := filepath.Base(inputFile) - for _, client := range clients { - tmpl, err := template.New("Test_Resources").ParseFiles(inputFile) - if err != nil { - return errors.Wrap(err, "parsing template from "+inputFile) - } - - buf := &bytes.Buffer{} - if err := tmpl.ExecuteTemplate(buf, fileName, client); err != nil { - return errors.Wrap(err, "executing template") - } - - err = ioutil.WriteFile(filepath.Join(outputDir, client.FilenamePrefix+".go"), buf.Bytes(), 0644) - if err != nil { - return errors.Wrap(err, "writing generated client bytes") - } - } - return nil -} diff --git a/pkg/storage/interface.go b/pkg/storage/interface.go deleted file mode 100644 index 54b8ab8..0000000 --- a/pkg/storage/interface.go +++ /dev/null @@ -1,32 +0,0 @@ -package storage - -import "github.com/solo-io/sqoop/pkg/api/types/v1" - -// Interface is interface to the storage backend -type Interface interface { - V1() V1 -} - -type V1 interface { - Register() error - Schemas() Schemas - ResolverMaps() ResolverMaps -} - -type Schemas interface { - Create(*v1.Schema) (*v1.Schema, error) - Update(*v1.Schema) (*v1.Schema, error) - Delete(name string) error - Get(name string) (*v1.Schema, error) - List() ([]*v1.Schema, error) - Watch(handlers ...SchemaEventHandler) (*Watcher, error) -} - -type ResolverMaps interface { - Create(*v1.ResolverMap) (*v1.ResolverMap, error) - Update(*v1.ResolverMap) (*v1.ResolverMap, error) - Delete(name string) error - Get(name string) (*v1.ResolverMap, error) - List() ([]*v1.ResolverMap, error) - Watch(...ResolverMapEventHandler) (*Watcher, error) -} diff --git a/pkg/storage/watcher.go b/pkg/storage/watcher.go deleted file mode 100644 index 5198604..0000000 --- a/pkg/storage/watcher.go +++ /dev/null @@ -1,87 +0,0 @@ -package storage - -import "github.com/solo-io/sqoop/pkg/api/types/v1" - -type Watcher struct { - runFunc func(stop <-chan struct{}, errs chan error) -} - -func NewWatcher(runFunc func(stop <-chan struct{}, errs chan error)) *Watcher { - return &Watcher{runFunc: runFunc} -} - -func (w *Watcher) Run(stop <-chan struct{}, errs chan error) { - w.runFunc(stop, errs) -} - -type SchemaEventHandler interface { - OnAdd(updatedList []*v1.Schema, obj *v1.Schema) - OnUpdate(updatedList []*v1.Schema, newObj *v1.Schema) - OnDelete(updatedList []*v1.Schema, obj *v1.Schema) -} - -type ResolverMapEventHandler interface { - OnAdd(updatedList []*v1.ResolverMap, obj *v1.ResolverMap) - OnUpdate(updatedList []*v1.ResolverMap, newObj *v1.ResolverMap) - OnDelete(updatedList []*v1.ResolverMap, obj *v1.ResolverMap) -} - -// SchemaEventHandlerFuncs is an adaptor to let you easily specify as many or -// as few of the notification functions as you want while still implementing -// SchemaEventHandler. -type SchemaEventHandlerFuncs struct { - AddFunc func(updatedList []*v1.Schema, obj *v1.Schema) - UpdateFunc func(updatedList []*v1.Schema, newObj *v1.Schema) - DeleteFunc func(updatedList []*v1.Schema, obj *v1.Schema) -} - -// OnAdd calls AddFunc if it's not nil. -func (r SchemaEventHandlerFuncs) OnAdd(updatedList []*v1.Schema, obj *v1.Schema) { - if r.AddFunc != nil { - r.AddFunc(updatedList, obj) - } -} - -// OnUpdate calls UpdateFunc if it's not nil. -func (r SchemaEventHandlerFuncs) OnUpdate(updatedList []*v1.Schema, newObj *v1.Schema) { - if r.UpdateFunc != nil { - r.UpdateFunc(updatedList, newObj) - } -} - -// OnDelete calls DeleteFunc if it's not nil. -func (r SchemaEventHandlerFuncs) OnDelete(updatedList []*v1.Schema, obj *v1.Schema) { - if r.DeleteFunc != nil { - r.DeleteFunc(updatedList, obj) - } -} - -// ResolverMapEventHandlerFuncs is an adaptor to let you easily specify as many or -// as few of the notification functions as you want while still implementing -// ResolverMapEventHandler. -type ResolverMapEventHandlerFuncs struct { - AddFunc func(updatedList []*v1.ResolverMap, obj *v1.ResolverMap) - UpdateFunc func(updatedList []*v1.ResolverMap, newObj *v1.ResolverMap) - DeleteFunc func(updatedList []*v1.ResolverMap, obj *v1.ResolverMap) -} - -// OnAdd calls AddFunc if it's not nil. -func (r ResolverMapEventHandlerFuncs) OnAdd(updatedList []*v1.ResolverMap, obj *v1.ResolverMap) { - if r.AddFunc != nil { - r.AddFunc(updatedList, obj) - } -} - -// OnUpdate calls UpdateFunc if it's not nil. -func (r ResolverMapEventHandlerFuncs) OnUpdate(updatedList []*v1.ResolverMap, newObj *v1.ResolverMap) { - if r.UpdateFunc != nil { - r.UpdateFunc(updatedList, newObj) - } -} - -// OnDelete calls DeleteFunc if it's not nil. -func (r ResolverMapEventHandlerFuncs) OnDelete(updatedList []*v1.ResolverMap, obj *v1.ResolverMap) { - if r.DeleteFunc != nil { - r.DeleteFunc(updatedList, obj) - } -} diff --git a/pkg/syncer/graphql_syncer.go b/pkg/syncer/graphql_syncer.go new file mode 100644 index 0000000..4ada8c2 --- /dev/null +++ b/pkg/syncer/graphql_syncer.go @@ -0,0 +1,150 @@ +package syncer + +import ( + "context" + + gloov1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1" + "github.com/solo-io/solo-kit/pkg/api/v1/clients" + "github.com/solo-io/solo-kit/pkg/api/v1/reporter" + "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" + "github.com/solo-io/solo-kit/pkg/errors" + "github.com/solo-io/solo-kit/pkg/utils/contextutils" + v1 "github.com/solo-io/sqoop/pkg/api/v1" + "github.com/solo-io/sqoop/pkg/engine" + "github.com/solo-io/sqoop/pkg/engine/router" + TODO "github.com/solo-io/sqoop/pkg/todo" + "github.com/solo-io/sqoop/pkg/translator" + "github.com/vektah/gqlgen/neelance/schema" +) + +type GraphQLSyncer struct { + writeNamespace string + reporter reporter.Reporter + writeErrs chan error + proxyReconciler gloov1.ProxyReconciler + resolverMapClient v1.ResolverMapClient + engine *engine.Engine + router *router.Router +} + +func NewGraphQLSyncer(writeNamespace string, + reporter reporter.Reporter, + writeErrs chan error, + proxyReconciler gloov1.ProxyReconciler, + resolverMapClient v1.ResolverMapClient, + engine *engine.Engine, + router *router.Router) v1.ApiSyncer { + s := &GraphQLSyncer{ + writeNamespace: writeNamespace, + reporter: reporter, + writeErrs: writeErrs, + proxyReconciler: proxyReconciler, + resolverMapClient: resolverMapClient, + engine: engine, + router: router, + } + return s +} + +func (s *GraphQLSyncer) Sync(ctx context.Context, snap *v1.ApiSnapshot) error { + ctx = contextutils.WithLogger(ctx, "syncer") + + logger := contextutils.LoggerFrom(ctx) + logger.Infof("begin sync %v (%v schemas %v resolverMaps)", + snap.Hash(), + len(snap.Schemas), + len(snap.ResolverMaps), + ) + defer logger.Infof("end sync %v", snap.Hash()) + logger.Debugf("%v", snap) + + resourceErrs := make(reporter.ResourceErrors) + + proxy := translator.Translate(s.writeNamespace, snap, resourceErrs) + logger.Infof("creating proxy %v", proxy.Metadata.Ref()) + if err := s.proxyReconciler.Reconcile(s.writeNamespace, gloov1.ProxyList{proxy}, TODO.TransitionFunction, clients.ListOpts{ + Ctx: ctx, + Selector: map[string]string{ + "created_by": "sqoop", + }, + }); err != nil { + return err + } + + var endpoints []*router.Endpoint + var resolverMapsToGenerate v1.ResolverMapList + var schemasToUpdate v1.SchemaList + for _, schema := range snap.Schemas.List() { + resolverMap, err := snap.ResolverMaps.List().Find(schema.Metadata.Ref().Strings()) + if err != nil { + newMeta := core.Metadata{ + Name: schema.Metadata.Name, + Namespace: schema.Metadata.Namespace, + Annotations: map[string]string{"created_for": schema.Metadata.Name}, + } + parsedSchema, err := parseSchemaString(schema) + if err != nil { + resourceErrs.AddError(schema, errors.Wrapf(err, "failed to parse schema")) + continue + } + + rm := translator.GenerateResolverMapSkeleton(newMeta, parsedSchema) + + resolverMapsToGenerate = append(resolverMapsToGenerate, rm) + schemasToUpdate = append(schemasToUpdate, schema) + + // nothing to do for this schema yet, need to receive some resolvers + continue + } + resourceErrs.Accept(schema) + + // this time should succeed + resolverMap, err = snap.ResolverMaps.List().Find(schema.Metadata.Ref().Strings()) + if err != nil { + resourceErrs.AddError(schema, errors.Wrapf(err, "finding resolvermap for schema")) + continue + } + + resourceErrs.Accept(resolverMap) + + endpoint, schemaErr, resolverErr := s.engine.CreateGraphqlEndpoint(schema, resolverMap) + if schemaErr != nil { + resourceErrs.AddError(schema, schemaErr) + } + if resolverErr != nil { + resourceErrs.AddError(resolverMap, resolverErr) + } + if schemaErr != nil || resolverErr != nil { + continue + } + endpoints = append(endpoints, endpoint) + } + if err := s.reporter.WriteReports(ctx, resourceErrs, nil); err != nil { + return errors.Wrapf(err, "writing reports") + } + if err := resourceErrs.Validate(); err != nil { + logger.Errorf("snapshot %v was rejected due to invalid config: %v", err) + return nil + } + + // final results + s.router.UpdateEndpoints(endpoints) + + // TODO(ilackarms): use reconciler, and allow resolvermaps to transition between snapshots + // then we can always generate ratehr than only generating for nonexisting! + for _, rm := range resolverMapsToGenerate { + if _, err := s.resolverMapClient.Write(rm, clients.WriteOpts{}); err != nil && !errors.IsExist(err) { + return errors.Wrapf(err, "writing generated resolver maps to storage") + } + } + + // start propagating for new set of resources + // TODO(ilackarms): reinstate propagator + return nil // s.propagator.PropagateStatuses(snap, proxy, clients.WatchOpts{Ctx: ctx}) + +} + +func parseSchemaString(sch *v1.Schema) (*schema.Schema, error) { + parsedSchema := schema.New() + return parsedSchema, parsedSchema.Parse(sch.InlineSchema) +} diff --git a/pkg/syncer/setup_syncer.go b/pkg/syncer/setup_syncer.go new file mode 100644 index 0000000..62c7093 --- /dev/null +++ b/pkg/syncer/setup_syncer.go @@ -0,0 +1,185 @@ +package syncer + +import ( + "context" + "fmt" + "net/http" + + "github.com/gogo/protobuf/types" + gloov1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1" + "github.com/solo-io/gloo/projects/gloo/pkg/bootstrap" + "github.com/solo-io/solo-kit/pkg/api/v1/clients" + "github.com/solo-io/solo-kit/pkg/api/v1/clients/factory" + "github.com/solo-io/solo-kit/pkg/api/v1/clients/kube" + "github.com/solo-io/solo-kit/pkg/api/v1/clients/memory" + "github.com/solo-io/solo-kit/pkg/api/v1/reporter" + "github.com/solo-io/solo-kit/pkg/utils/contextutils" + "github.com/solo-io/solo-kit/pkg/utils/errutils" + v1 "github.com/solo-io/sqoop/pkg/api/v1" + "github.com/solo-io/sqoop/pkg/defaults" + "github.com/solo-io/sqoop/pkg/engine" + "github.com/solo-io/sqoop/pkg/engine/router" + TODO "github.com/solo-io/sqoop/pkg/todo" + "k8s.io/client-go/rest" +) + +type Opts struct { + WriteNamespace string + WatchNamespaces []string + Schemas factory.ResourceClientFactory + ResolverMaps factory.ResourceClientFactory + Proxies factory.ResourceClientFactory + WatchOpts clients.WatchOpts + DevMode bool + SidecarAddr string +} + +func Setup(ctx context.Context, kubeCache kube.SharedCache, cache memory.InMemoryResourceCache, settings *gloov1.Settings) error { + var ( + cfg *rest.Config + ) + proxyFactory, err := bootstrap.ConfigFactoryForSettings( + settings, + cache, + kubeCache, + gloov1.ProxyCrd, + &cfg, + ) + if err != nil { + return err + } + + schemaFactory, err := bootstrap.ConfigFactoryForSettings( + settings, + cache, + kubeCache, + v1.SchemaCrd, + &cfg, + ) + if err != nil { + return err + } + + resolverMapFactory, err := bootstrap.ConfigFactoryForSettings( + settings, + cache, + kubeCache, + v1.ResolverMapCrd, + &cfg, + ) + if err != nil { + return err + } + + refreshRate, err := types.DurationFromProto(settings.RefreshRate) + if err != nil { + return err + } + + writeNamespace := settings.DiscoveryNamespace + if writeNamespace == "" { + writeNamespace = defaults.GlooSystem + } + watchNamespaces := settings.WatchNamespaces + var writeNamespaceProvided bool + for _, ns := range watchNamespaces { + if ns == writeNamespace { + writeNamespaceProvided = true + break + } + } + if !writeNamespaceProvided { + watchNamespaces = append(watchNamespaces, writeNamespace) + } + opts := Opts{ + WriteNamespace: writeNamespace, + WatchNamespaces: watchNamespaces, + Schemas: schemaFactory, + ResolverMaps: resolverMapFactory, + Proxies: proxyFactory, + WatchOpts: clients.WatchOpts{ + Ctx: ctx, + RefreshRate: refreshRate, + }, + SidecarAddr: fmt.Sprintf("%v:%v", "127.0.0.1", TODO.SqoopSidecarBindPort), + } + return RunSqoop(opts) +} + +func RunSqoop(opts Opts) error { + watchNamespaces := opts.WatchNamespaces + opts.WatchOpts = opts.WatchOpts.WithDefaults() + opts.WatchOpts.Ctx = contextutils.WithLogger(opts.WatchOpts.Ctx, "gateway") + + // TODO(ilackarms): this piece (initializing clients) should really be generated by solo-kit + proxyClient, err := gloov1.NewProxyClient(opts.Proxies) + if err != nil { + return err + } + if err := proxyClient.Register(); err != nil { + return err + } + + schemaClient, err := v1.NewSchemaClient(opts.Schemas) + if err != nil { + return err + } + if err := schemaClient.Register(); err != nil { + return err + } + + resolverMapClient, err := v1.NewResolverMapClient(opts.ResolverMaps) + if err != nil { + return err + } + if err := resolverMapClient.Register(); err != nil { + return err + } + + proxyReconciler := gloov1.NewProxyReconciler(proxyClient) + + emitter := v1.NewApiEmitter(resolverMapClient, schemaClient) + + rpt := reporter.NewReporter("sqoop", resolverMapClient.BaseClient(), schemaClient.BaseClient()) + writeErrs := make(chan error) + /* + proxyReconciler: proxyReconciler, + engine: engine, + router: router, + */ + eng := engine.NewEngine(opts.SidecarAddr) + + rtr := router.NewRouter() + + sync := NewGraphQLSyncer(opts.WriteNamespace, rpt, writeErrs, proxyReconciler, resolverMapClient, eng, rtr) + + go func() { + logger := contextutils.LoggerFrom(opts.WatchOpts.Ctx) + logger.Infof("starting graphql server on %d", TODO.SqoopServerBindPort) + if err := http.ListenAndServe(fmt.Sprintf(":%d", TODO.SqoopServerBindPort), rtr); err != nil { + logger.Fatalf("failed starting sqoop server: %v", err) + } + }() + + eventLoop := v1.NewApiEventLoop(emitter, sync) + eventLoopErrs, err := eventLoop.Run(watchNamespaces, opts.WatchOpts) + if err != nil { + return err + } + go errutils.AggregateErrs(opts.WatchOpts.Ctx, writeErrs, eventLoopErrs, "event_loop") + + logger := contextutils.LoggerFrom(opts.WatchOpts.Ctx) + + go func() { + for { + select { + case err := <-writeErrs: + logger.Errorf("error: %v", err) + case <-opts.WatchOpts.Ctx.Done(): + close(writeErrs) + return + } + } + }() + return nil +} diff --git a/pkg/todo/todos.go b/pkg/todo/todos.go new file mode 100644 index 0000000..69848b7 --- /dev/null +++ b/pkg/todo/todos.go @@ -0,0 +1,22 @@ +package TODO + +import ( + v1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1" +) + +const SqoopSidecarBindAddr = "127.0.0.1" +const SqoopSidecarBindPort = 9090 +const SqoopServerBindPort = 9095 + +// TODO(ilackarms): make this a global function +func TransitionFunction(original, desired *v1.Proxy) (bool, error) { + if len(original.Listeners) != len(desired.Listeners) { + return true, nil + } + for i := range original.Listeners { + if !original.Listeners[i].Equal(desired.Listeners[i]) { + return true, nil + } + } + return false, nil +} diff --git a/pkg/util/generate_resolver_map.go b/pkg/translator/generate_resolver_map.go similarity index 51% rename from pkg/util/generate_resolver_map.go rename to pkg/translator/generate_resolver_map.go index 76e0030..6a06248 100644 --- a/pkg/util/generate_resolver_map.go +++ b/pkg/translator/generate_resolver_map.go @@ -1,22 +1,24 @@ -package util +package translator import ( - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/exec" + "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" + v1 "github.com/solo-io/sqoop/pkg/api/v1" + "github.com/solo-io/sqoop/pkg/engine/exec" "github.com/vektah/gqlgen/neelance/schema" ) -func GenerateResolverMapSkeleton(name string, sch *schema.Schema) *v1.ResolverMap { +// TODO(ilackarms) +func GenerateResolverMapSkeleton(meta core.Metadata, sch *schema.Schema) *v1.ResolverMap { types := make(map[string]*v1.TypeResolver) for _, t := range sch.Types { if exec.MetaType(t.TypeName()) { continue } - fields := make(map[string]*v1.Resolver) + fields := make(map[string]*v1.FieldResolver) switch t := t.(type) { case *schema.Object: for _, f := range t.Fields { - fields[f.Name] = &v1.Resolver{ + fields[f.Name] = &v1.FieldResolver{ Resolver: nil, } } @@ -27,7 +29,7 @@ func GenerateResolverMapSkeleton(name string, sch *schema.Schema) *v1.ResolverMa types[t.TypeName()] = &v1.TypeResolver{Fields: fields} } return &v1.ResolverMap{ - Name: name, - Types: types, + Metadata: meta, + Types: types, } } diff --git a/pkg/translator/routes.go b/pkg/translator/routes.go new file mode 100644 index 0000000..82803cd --- /dev/null +++ b/pkg/translator/routes.go @@ -0,0 +1,51 @@ +package translator + +import ( + "fmt" + "sort" + + gloov1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1" + "github.com/solo-io/solo-kit/pkg/api/v1/reporter" + "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" + "github.com/solo-io/solo-kit/pkg/errors" + v1 "github.com/solo-io/sqoop/pkg/api/v1" +) + +type route struct { + path string + action *gloov1.RouteAction +} + +func RoutePath(resolverMap core.ResourceRef, typeName, fieldName string) string { + return fmt.Sprintf("/%v.%v.%v.%v", resolverMap.Namespace, resolverMap.Name, typeName, fieldName) +} + +func routesForResolverMaps(resolverMaps v1.ResolverMapList, resourceErrs reporter.ResourceErrors) []route { + var routes []route + for _, resolverMap := range resolverMaps { + for typeName, typeResolver := range resolverMap.Types { + for fieldName, fieldResolver := range typeResolver.Fields { + glooResolver, ok := fieldResolver.Resolver.(*v1.FieldResolver_GlooResolver) + if !ok { + continue + } + if glooResolver.GlooResolver == nil { + resourceErrs.AddError(resolverMap, errors.Errorf("gloo resolver cannot be nil")) + continue + } + if glooResolver.GlooResolver.Action == nil { + resourceErrs.AddError(resolverMap, errors.Errorf("resolver action cannot be nil")) + continue + } + routes = append(routes, route{ + path: RoutePath(resolverMap.Metadata.Ref(), typeName, fieldName), + action: glooResolver.GlooResolver.Action, + }) + } + } + } + sort.SliceStable(routes, func(i, j int) bool { + return routes[i].path < routes[j].path + }) + return routes +} diff --git a/pkg/translator/translator.go b/pkg/translator/translator.go new file mode 100644 index 0000000..04eecbd --- /dev/null +++ b/pkg/translator/translator.go @@ -0,0 +1,60 @@ +package translator + +import ( + gloov1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1" + "github.com/solo-io/solo-kit/pkg/api/v1/reporter" + "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" + v1 "github.com/solo-io/sqoop/pkg/api/v1" + TODO "github.com/solo-io/sqoop/pkg/todo" +) + +// trnslate a snapshot of schemas and resolvermaps to: +// a 1 proxy for the snapshot, assigned to the sqoop sidecar +func Translate(writeNamespace string, snap *v1.ApiSnapshot, resourceErrs reporter.ResourceErrors) *gloov1.Proxy { + ourRoutes := routesForResolverMaps(snap.ResolverMaps.List(), resourceErrs) + + var routes []*gloov1.Route + + for _, r := range ourRoutes { + routes = append(routes, &gloov1.Route{ + Matcher: &gloov1.Matcher{ + PathSpecifier: &gloov1.Matcher_Exact{ + Exact: r.path, + }, + Methods: []string{"POST"}, + }, + Action: &gloov1.Route_RouteAction{RouteAction: r.action}, + }) + } + + return &gloov1.Proxy{ + Metadata: core.Metadata{ + Name: "sqoop-proxy", + Namespace: writeNamespace, + Labels: map[string]string{ + "created_by": "sqoop", + }, + }, + Listeners: []*gloov1.Listener{ + { + // TODO (ilackarms): make this section configurable + Name: "sqoop-listener", + BindAddress: TODO.SqoopSidecarBindAddr, + BindPort: TODO.SqoopSidecarBindPort, + ListenerType: &gloov1.Listener_HttpListener{ + HttpListener: &gloov1.HttpListener{ + VirtualHosts: []*gloov1.VirtualHost{ + { + Name: "sqoop-vhost", + Domains: []string{"*"}, + Routes: routes, + }, + }, + }, + }, + // TODO(ilackarms / yuval-k): decide if we need ssl for connecting to sidecar + SslConfiguations: nil, + }, + }, + } +} diff --git a/test/kube_e2e/helpers.go b/test/kube_e2e/helpers.go deleted file mode 100644 index 0688a15..0000000 --- a/test/kube_e2e/helpers.go +++ /dev/null @@ -1,152 +0,0 @@ -package kube_e2e - -import ( - "bytes" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "text/template" - - "github.com/onsi/ginkgo" - "github.com/pkg/errors" - "github.com/solo-io/gloo/pkg/log" - "github.com/solo-io/gloo/test/helpers" -) - -const ( - // gloo labels - testrunner = "testrunner" - controlPlane = "control-plane" - sqoopContainer = "sqoop" - upstreamDiscovery = "upstream-discovery" - funcitonDiscovery = "function-discovery" - starWars = "starwars" -) - -func SetupKubeForE2eTest(namespace string, buildImages, push, debug bool) error { - if err := helpers.SetupKubeForTest(namespace); err != nil { - return err - } - if buildImages { - if err := BuildPushContainers(push, debug); err != nil { - return err - } - } - kubeResourcesDir := filepath.Join(KubeE2eDirectory(), "kube_resources") - - envoyImageTag := os.Getenv("ENVOY_IMAGE_TAG") - if envoyImageTag == "" { - log.Warnf("no ENVOY_IMAGE_TAG specified, defaulting to latest") - envoyImageTag = "latest" - } - - pullPolicy := "IfNotPresent" - - if push { - pullPolicy = "Always" - } - - data := templateData{Namespace: namespace, ImageTag: helpers.ImageTag(), ImagePullPolicy: pullPolicy, Debug: ""} - if debug { - data.Debug = "-debug" - } - - testingResources := "testing-resources.yaml" - installResources := "test-install.yaml" - - if err := GenerateKubeYaml(kubeResourcesDir, "testing-resources.tmpl.yaml", testingResources, data); err != nil { - return err - } - - if err := GenerateKubeYaml(kubeResourcesDir, "install.tmpl.yaml", installResources, data); err != nil { - return err - } - - if err := helpers.Kubectl("apply", "-f", filepath.Join(kubeResourcesDir, installResources)); err != nil { - return errors.Wrapf(err, "creating kube resource from install.yml") - } - if err := helpers.Kubectl("apply", "-f", filepath.Join(kubeResourcesDir, testingResources)); err != nil { - return errors.Wrapf(err, "creating kube resource from testing-resources.yml") - } - if err := helpers.WaitPodsRunning( - testrunner, - starWars, - ); err != nil { - return errors.Wrap(err, "waiting for pods to start") - } - - if err := helpers.WaitPodsRunning( - controlPlane, - sqoopContainer, - upstreamDiscovery, - funcitonDiscovery, - ); err != nil { - return errors.Wrap(err, "waiting for pods to start") - } - return nil -} - -func SqoopSDirectory() string { - return filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "solo-io", "sqoop") -} - -func KubeE2eDirectory() string { - return filepath.Join(SqoopSDirectory(), "test", "kube_e2e") -} - -// builds and pushes all docker containers needed for test -func BuildPushContainers(push, debug bool) error { - if os.Getenv("SKIP_BUILD") == "1" { - return nil - } - imageTag := helpers.ImageTag() - os.Setenv("IMAGE_TAG", imageTag) - - // make the gloo containers - for _, component := range []string{"sqoop"} { - target := component - target += "-docker" - if push { - target += "-push" - } - - if debug { - target += "-debug" - } - - cmd := exec.Command("make", target) - cmd.Dir = SqoopSDirectory() - cmd.Stdout = ginkgo.GinkgoWriter - cmd.Stderr = ginkgo.GinkgoWriter - if err := cmd.Run(); err != nil { - return err - } - } - return nil -} - -type templateData struct { - Namespace string - ImageTag string - ImagePullPolicy string - Debug string -} - -func GenerateKubeYaml(kubeResourcesDir string, templateFile, outFile string, data templateData) error { - testingResourcesTmpl, err := template.New("Test_Resources").ParseFiles(filepath.Join(kubeResourcesDir, templateFile)) - if err != nil { - return errors.Wrapf(err, "parsing template from %s", templateFile) - } - - buf := &bytes.Buffer{} - if err := testingResourcesTmpl.ExecuteTemplate(buf, templateFile, data); err != nil { - return errors.Wrapf(err, "executing template") - } - - err = ioutil.WriteFile(filepath.Join(kubeResourcesDir, outFile), buf.Bytes(), 0644) - if err != nil { - return errors.Wrapf(err, "writing generated test resources template") - } - return nil -} diff --git a/test/kube_e2e/kube_resources/install.tmpl.yaml b/test/kube_e2e/kube_resources/install.tmpl.yaml deleted file mode 100644 index f24cbc5..0000000 --- a/test/kube_e2e/kube_resources/install.tmpl.yaml +++ /dev/null @@ -1,434 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: schemas.sqoop.solo.io -spec: - group: sqoop.solo.io - names: - kind: Schema - listKind: SchemaList - plural: schemas - singular: schema - scope: Namespaced - version: v1 - ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: resolvermaps.sqoop.solo.io -spec: - group: sqoop.solo.io - names: - kind: ResolverMap - listKind: ResolverMapList - plural: resolvermaps - singular: resolvermap - scope: Namespaced - version: v1 - ---- -#rbac for function-discovery -kind: ClusterRole -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: sqoop-role -rules: -- apiGroups: ["sqoop.solo.io"] - resources: ["schemas", "resolvermaps"] - verbs: ["*"] -- apiGroups: ["gloo.solo.io"] - resources: ["virtualservices"] - verbs: ["*"] - ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: sqoop-cluster-binding -subjects: -- kind: ServiceAccount - name: default - namespace: {{ .Namespace }} -roleRef: - kind: ClusterRole - name: sqoop-role - apiGroup: rbac.authorization.k8s.io - ---- - -apiVersion: apps/v1beta2 -kind: Deployment -metadata: - name: sqoop - namespace: {{ .Namespace }} - labels: - gloo: sqoop -spec: - replicas: 1 - selector: - matchLabels: - gloo: sqoop - template: - metadata: - labels: - gloo: sqoop - spec: - initContainers: - - name: assignnodeid - image: busybox - command: - - sed - - s/NODE_ID_PLACE_HOLDER/$(POD_NAME).$(POD_NAMESPACE)/;w /config-tmp/envoy.yaml - - "/config/envoy.yaml" - volumeMounts: - - name: sqoop-proxy-config - mountPath: /config - - name: config-tmp - mountPath: /config-tmp - env: - - name: POD_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.namespace - containers: - - name: proxy - image: "soloio/envoy:0.4.4" - imagePullPolicy: IfNotPresent - ports: - - containerPort: 8080 - name: http - - containerPort: 8443 - name: https - - containerPort: 19000 - name: admin - command: ["envoy"] - args: ["-c", "/config/envoy.yaml", "--v2-config-only"] - volumeMounts: - - name: config-tmp - mountPath: /config - - name: sqoop - image: "soloio/sqoop:{{ .ImageTag }}" - imagePullPolicy: IfNotPresent - ports: - - containerPort: 9090 - name: http - args: - - "--storage.type=kube" - - "--storage.refreshrate=1m" - - "--kube.namespace={{ .Namespace }}" - volumeMounts: - - name: config-tmp - mountPath: /config - volumes: - - name: sqoop-proxy-config - configMap: - name: sqoop-proxy-config - - name: config-tmp - emptyDir: {} ---- -# GW -apiVersion: v1 -kind: Service -metadata: - name: sqoop - namespace: {{ .Namespace }} - labels: - gloo: sqoop -spec: - type: LoadBalancer - ports: - - port: 9090 - protocol: TCP - name: http - selector: - gloo: sqoop - - ---- - -apiVersion: v1 -kind: ConfigMap -metadata: - name: sqoop-proxy-config - namespace: {{ .Namespace }} -data: - envoy.yaml: | - node: - cluster: sqoop - id: sqoop~NODE_ID_PLACE_HOLDER - static_resources: - clusters: - - name: xds_cluster - connect_timeout: 5.000s - hosts: - - socket_address: - address: control-plane - port_value: 8081 - http2_protocol_options: {} - type: STRICT_DNS - dynamic_resources: - ads_config: - api_type: GRPC - grpc_services: - - envoy_grpc: {cluster_name: xds_cluster} - cds_config: - ads: {} - lds_config: - ads: {} - admin: - access_log_path: /dev/null - address: - socket_address: - address: 0.0.0.0 - port_value: 19000 - - -### Gloo - ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: upstreams.gloo.solo.io -spec: - group: gloo.solo.io - names: - kind: Upstream - listKind: UpstreamList - plural: upstreams - singular: upstream - scope: Namespaced - version: v1 - ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: virtualservices.gloo.solo.io -spec: - group: gloo.solo.io - names: - kind: VirtualService - listKind: VirtualServiceList - plural: virtualservices - singular: virtualservice - scope: Namespaced - version: v1 ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: roles.gloo.solo.io -spec: - group: gloo.solo.io - names: - kind: Role - listKind: RoleList - plural: roles - singular: role - scope: Namespaced - version: v1 - ---- -# Source: gloo/templates/rbac.yaml ---- -#rbac for control-plane -kind: ClusterRole -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: gloo-role -rules: -- apiGroups: [""] - resources: ["pods", "services", "secrets", "endpoints", "configmaps"] - verbs: ["get", "watch", "list"] -- apiGroups: [""] - resources: ["namespaces"] - verbs: ["get", "create"] -- apiGroups: ["apiextensions.k8s.io"] - resources: ["customresourcedefinitions"] - verbs: ["get", "create"] -- apiGroups: ["gloo.solo.io"] - resources: ["upstreams", "virtualservices", "roles"] - verbs: ["*"] ---- -#rbac for function-discovery -kind: ClusterRole -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: gloo-discovery-role -rules: -- apiGroups: [""] - resources: ["pods", "services", "endpoints"] - verbs: ["get", "watch", "list"] -- apiGroups: [""] - resources: ["secrets", "configmaps"] - verbs: ["*"] -- apiGroups: ["extensions"] - resources: ["ingresses"] - verbs: ["get", "watch", "list"] -- apiGroups: ["gloo.solo.io"] - resources: ["upstreams", "virtualservices"] - verbs: ["*"] - ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: gloo-cluster-admin-binding -subjects: -- kind: ServiceAccount - name: default - namespace: {{ .Namespace }} -roleRef: - kind: ClusterRole - name: gloo-role - apiGroup: rbac.authorization.k8s.io - ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: gloo-discovery-cluster-admin-binding -subjects: -- kind: ServiceAccount - name: default - namespace: {{ .Namespace }} -roleRef: - kind: ClusterRole - name: gloo-discovery-role - apiGroup: rbac.authorization.k8s.io - ---- -# Source: gloo/templates/control-plane.yaml -apiVersion: apps/v1beta2 -kind: Deployment -metadata: - name: control-plane - namespace: {{ .Namespace }} - labels: - gloo: control-plane -spec: - replicas: 1 - selector: - matchLabels: - gloo: control-plane - template: - metadata: - labels: - gloo: control-plane - spec: - containers: - - name: control-plane - image: "soloio/control-plane:0.4.4" - imagePullPolicy: IfNotPresent - ports: - - containerPort: 8081 - name: http - env: - - name: GRPC_TRACE - value: "all" - - name: DEBUG - value: "1" - args: - - "--storage.type=kube" - - "--storage.refreshrate=1m" - - "--secrets.type=kube" - - "--secrets.refreshrate=1m" - - "--files.type=kube" - - "--files.refreshrate=1m" - - "--xds.port=8081" - - "--kube.namespace={{ .Namespace }}" ---- -apiVersion: v1 -kind: Service -metadata: - name: control-plane - namespace: {{ .Namespace }} - labels: - gloo: control-plane -spec: - ports: - - port: 8081 - protocol: TCP - name: http - selector: - gloo: control-plane ---- -# Source: gloo/templates/function-discovery.yaml - -apiVersion: apps/v1beta2 -kind: Deployment -metadata: - name: function-discovery - namespace: {{ .Namespace }} - labels: - gloo: function-discovery -spec: - replicas: 1 - selector: - matchLabels: - gloo: function-discovery - template: - metadata: - labels: - gloo: function-discovery - spec: - containers: - - name: function-discovery - image: "soloio/function-discovery:0.4.4" - imagePullPolicy: IfNotPresent - env: - - name: GRPC_TRACE - value: "all" - - name: DEBUG - value: "1" - args: - - "--storage.type=kube" - - "--storage.refreshrate=30m" - - "--secrets.type=kube" - - "--secrets.refreshrate=30m" - - "--files.type=kube" - - "--files.refreshrate=30m" - - "--kube.namespace={{ .Namespace }}" - ---- -# Source: gloo/templates/upstream-discovery.yaml - -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - labels: - gloo: upstream-discovery - name: upstream-discovery - namespace: {{ .Namespace }} -spec: - selector: - matchLabels: - gloo: upstream-discovery - replicas: 1 - template: - metadata: - labels: - gloo: upstream-discovery - spec: - containers: - - image: soloio/upstream-discovery:0.4.4 - imagePullPolicy: IfNotPresent - name: upstream-discovery - env: - - name: GRPC_TRACE - value: "all" - - name: DEBUG - value: "1" - args: - - "--storage.type=kube" - - "--storage.refreshrate=30m" - - "--kube.namespace={{ .Namespace }}" diff --git a/test/kube_e2e/kube_resources/testing-resources.tmpl.yaml b/test/kube_e2e/kube_resources/testing-resources.tmpl.yaml deleted file mode 100644 index e2804cf..0000000 --- a/test/kube_e2e/kube_resources/testing-resources.tmpl.yaml +++ /dev/null @@ -1,304 +0,0 @@ -########################## -# # -# Example # -# Service # -# # -# # -########################## -# starwars service -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - labels: - gloo: starwars - name: starwars - namespace: {{ .Namespace }} -spec: - selector: - matchLabels: - gloo: starwars - replicas: 1 - template: - metadata: - labels: - gloo: starwars - spec: - containers: - - image: soloio/starwars-example:latest - name: starwars - ports: - - containerPort: 1234 - name: http ---- -apiVersion: v1 -kind: Service -metadata: - name: starwars - namespace: {{ .Namespace }} - labels: - sevice: starwars -spec: - ports: - - port: 1234 - protocol: TCP - selector: - gloo: starwars ---- -apiVersion: gloo.solo.io/v1 -kind: Upstream -metadata: - name: starwars - namespace: {{ .Namespace }} -spec: - spec: - service_name: starwars - service_namespace: {{ .Namespace }} - service_port: 1234 - type: kubernetes - service_info: - type: REST - functions: - - name: GetHero - spec: - headers: - :method: GET - path: /api/hero - - name: GetCharacter - spec: - body: "" - path: /api/character/ - headers: - "x-id": "{{ "{{" }}id {{ "}}" }}" - :method: GET - - name: GetCharacters - spec: - headers: - :method: POST - path: /api/characters ---- -apiVersion: sqoop.solo.io/v1 -kind: Schema -metadata: - name: starwars - namespace: {{ .Namespace }} -spec: - resolver_map: starwars-resolvers - inline_schema: | - # The query type, represents all of the entry points into our object graph - type Query { - hero(episode: Episode = NEWHOPE): Character - reviews(episode: Episode!, since: Time): [Review]! - search(text: String!): [SearchResult]! - character(id: ID!): Character - droid(id: ID!): Droid - human(id: ID!): Human - starship(id: ID!): Starship - } - # The mutation type, represents all updates we can make to our data - type Mutation { - createReview(episode: Episode!, review: ReviewInput!): Review - } - # The episodes in the Star Wars trilogy - enum Episode { - # Star Wars Episode IV: A New Hope, released in 1977. - NEWHOPE - # Star Wars Episode V: The Empire Strikes Back, released in 1980. - EMPIRE - # Star Wars Episode VI: Return of the Jedi, released in 1983. - JEDI - } - # A character from the Star Wars universe - interface Character { - # The ID of the character - id: ID! - # The name of the character - name: String! - # The friends of the character, or an empty list if they have none - friends: [Character] - # The friends of the character exposed as a connection with edges - friendsConnection(first: Int, after: ID): FriendsConnection! - # The movies this character appears in - appearsIn: [Episode!]! - } - # Units of height - enum LengthUnit { - # The standard unit around the world - METER - # Primarily used in the United States - FOOT - } - # A humanoid creature from the Star Wars universe - type Human implements Character { - # The ID of the human - id: ID! - # What this human calls themselves - name: String! - # Height in the preferred unit, default is meters - height(unit: LengthUnit = METER): Float! - # Mass in kilograms, or null if unknown - mass: Float - # This human` + "`" + `s friends, or an empty list if they have none - friends: [Character] - # The friends of the human exposed as a connection with edges - friendsConnection(first: Int, after: ID): FriendsConnection! - # The movies this human appears in - appearsIn: [Episode!]! - # A list of starships this person has piloted, or an empty list if none - starships: [Starship] - } - # An autonomous mechanical character in the Star Wars universe - type Droid implements Character { - # The ID of the droid - id: ID! - # What others call this droid - name: String! - # This droid` + "`" + `s friends, or an empty list if they have none - friends: [Character] - # The friends of the droid exposed as a connection with edges - friendsConnection(first: Int, after: ID): FriendsConnection! - # The movies this droid appears in - appearsIn: [Episode!]! - # This droid` + "`" + `s primary function - primaryFunction: String - } - # A connection object for a character` + "`" + `s friends - type FriendsConnection { - # The total number of friends - totalCount: Int! - # The edges for each of the character` + "`" + `s friends. - edges: [FriendsEdge] - # A list of the friends, as a convenience when edges are not needed. - friends: [Character] - # Information for paginating this connection - pageInfo: PageInfo! - } - # An edge object for a character` + "`" + `s friends - type FriendsEdge { - # A cursor used for pagination - cursor: ID! - # The character represented by this friendship edge - node: Character - } - # Information for paginating this connection - type PageInfo { - startCursor: ID! - endCursor: ID! - hasNextPage: Boolean! - } - # Represents a review for a movie - type Review { - # The number of stars this review gave, 1-5 - stars: Int! - # Comment about the movie - commentary: String - # when the review was posted - time: Time - } - # The input object sent when someone is creating a new review - input ReviewInput { - # 0-5 stars - stars: Int! - # Comment about the movie, optional - commentary: String - # when the review was posted - time: Time - } - type Starship { - # The ID of the starship - id: ID! - # The name of the starship - name: String! - # Length of the starship, along the longest axis - length(unit: LengthUnit = METER): Float! - # coordinates tracking this ship - history: [[Int]] - } - union SearchResult = Human | Droid | Starship - scalar Time ---- - -apiVersion: sqoop.solo.io/v1 -kind: ResolverMap -metadata: - name: starwars-resolvers - namespace: {{ .Namespace }} -spec: - types: - Droid: - fields: - appearsIn: {} - friends: {} - friendsConnection: {} - id: {} - name: {} - primaryFunction: {} - FriendsConnection: - fields: - edges: {} - friends: {} - pageInfo: {} - totalCount: {} - FriendsEdge: - fields: - cursor: {} - node: {} - Human: - fields: - appearsIn: {} - friends: {} - friendsConnection: {} - height: {} - id: {} - mass: {} - name: {} - starships: {} - Mutation: - fields: - createReview: {} - PageInfo: - fields: - endCursor: {} - hasNextPage: {} - startCursor: {} - Query: - fields: - character: {} - droid: {} - hero: - gloo_resolver: - single_function: - function: GetHero - upstream: starwars - human: {} - reviews: {} - search: {} - starship: {} - Review: - fields: - commentary: {} - stars: {} - time: {} - Starship: - fields: - history: {} - id: {} - length: {} - name: {} - ---- -apiVersion: v1 -kind: Pod -metadata: - labels: - gloo: testrunner - name: testrunner - namespace: {{.Namespace}} -spec: - containers: - - image: soloio/testrunner:{{ .ImageTag }} - imagePullPolicy: {{ .ImagePullPolicy }} - command: - - sleep - - "36000" - name: testrunner - restartPolicy: Always diff --git a/test/kube_e2e/kubernetes_e2e_test.go b/test/kube_e2e/kubernetes_e2e_test.go deleted file mode 100644 index a45cb97..0000000 --- a/test/kube_e2e/kubernetes_e2e_test.go +++ /dev/null @@ -1,198 +0,0 @@ -package kube_e2e - -import ( - "os/exec" - "regexp" - "time" - - "os" - "path/filepath" - - "strings" - - "fmt" - "math/rand" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "github.com/solo-io/gloo/pkg/log" - "github.com/solo-io/gloo/pkg/storage" - "github.com/solo-io/gloo/pkg/storage/crd" - "github.com/solo-io/gloo/test/helpers" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/clientcmd" - sqoopstorage "github.com/solo-io/sqoop/pkg/storage" - sqoopcrd "github.com/solo-io/sqoop/pkg/storage/crd" -) - -var ( - // namespace needs to be set here or tests will break - namespace = getNamespace() -) - -func getNamespace() string { - rand.Seed(time.Now().UTC().UnixNano()) - return helpers.RandString(6) -} - -var gloo storage.Interface -var sqoop sqoopstorage.Interface -var kube kubernetes.Interface - -var _ = BeforeSuite(func() { - log.Printf("USING IMAGE TAG %v", helpers.ImageTag()) - - // are we on minikube? set docket env vars and push to false - push := true - debug := false - if setupMinikubeEnvVars() { - push = false - } - if os.Getenv("DEBUG_IMAGES") == "1" { - debug = true - } - - log.Debugf("SetupKubeForE2eTest: push = %v \t namespace = %v", push, namespace) - - err := SetupKubeForE2eTest(namespace, true, push, debug) - helpers.Must(err) - kubeconfigPath := filepath.Join(os.Getenv("HOME"), ".kube", "config") - masterUrl := "" - helpers.Must(err) - cfg, err := clientcmd.BuildConfigFromFlags(masterUrl, kubeconfigPath) - helpers.Must(err) - gloo, err = crd.NewStorage(cfg, namespace, time.Minute) - helpers.Must(err) - sqoop, err = sqoopcrd.NewStorage(cfg, namespace, time.Minute) - helpers.Must(err) - kube, err = kubernetes.NewForConfig(cfg) - helpers.Must(err) -}) - -var _ = AfterSuite(func() { - helpers.TeardownKubeE2E(namespace) -}) - -// clean up function discovery's cached upstream names -var _ = BeforeEach(func() { - helpers.KubectlOut("delete", "pod", "-l", "gloo=function-discovery") -}) - -type curlOpts struct { - protocol string - path string - method string - host string - service string - caFile string - body string - headers map[string]string - port int - returnHeaders bool -} - -func curlEventuallyShouldRespond(opts curlOpts, substr string, timeout ...time.Duration) { - t := time.Second * 20 - if len(timeout) > 0 { - t = timeout[0] - } - // for some useful-ish output - tick := time.Tick(t / 8) - Eventually(func() string { - res, err := curl(opts) - if err != nil { - res = err.Error() - } - select { - default: - break - case <-tick: - log.GreyPrintf("running: %v\nwant %v\nhave: %s", opts, substr, res) - } - if strings.Contains(res, substr) { - log.GreyPrintf("success: %v", res) - } - return res - }, t, "5s").Should(ContainSubstring(substr)) -} - -func curl(opts curlOpts) (string, error) { - args := []string{"curl", "-v", "--connect-timeout", "10", "--max-time", "10"} - - if opts.returnHeaders { - args = append(args, "-I") - } - - if opts.method != "GET" && opts.method != "" { - args = append(args, "-X"+opts.method) - } - if opts.host != "" { - args = append(args, "-H", "Host: "+opts.host) - } - if opts.caFile != "" { - args = append(args, "--cacert", opts.caFile) - } - if opts.body != "" { - args = append(args, "-H", "Content-Type: application/json") - args = append(args, "-d", opts.body) - } - for h, v := range opts.headers { - args = append(args, "-H", fmt.Sprintf("%v: %v", h, v)) - } - port := opts.port - if port == 0 { - port = 8080 - } - protocol := opts.protocol - if protocol == "" { - protocol = "http" - } - service := opts.service - if service == "" { - service = "sqoop" - } - args = append(args, fmt.Sprintf("%v://%s:%v%s", protocol, service, port, opts.path)) - log.Debugf("running: curl %v", strings.Join(args, " ")) - return helpers.TestRunner(args...) -} - -func setupMinikubeEnvVars() bool { - if os.Getenv("FORCE_PUSH") == "1" { - return false - } - // are we in minikube? - out, _ := exec.Command("kubectl", "config", "current-context").CombinedOutput() - if strings.Contains(string(out), "minikube") { - return setupEnvFromMinikube() - } - return false -} - -var lineregex = regexp.MustCompile("export (\\S+)=\"(.+)\"") - -func setupEnvFromMinikube() bool { - out, err := exec.Command("minikube", "docker-env", "--shell", "bash").CombinedOutput() - if err != nil { - return false - } - outs := string(out) - varsset := false - lines := strings.Split(outs, "\n") - const prefix = "export " - for _, line := range lines { - line = strings.TrimSpace(line) - if len(line) == 0 { - continue - } - if line[0] == '#' { - continue - } - matches := lineregex.FindStringSubmatch(line) - if matches != nil { - varsset = true - log.Debugf("Settings var: %v %v", matches[1], matches[2]) - os.Setenv(matches[1], matches[2]) - } - } - return varsset -} diff --git a/test/kube_e2e/kubernetes_happy_path_test.go b/test/kube_e2e/kubernetes_happy_path_test.go deleted file mode 100644 index bd8c6d3..0000000 --- a/test/kube_e2e/kubernetes_happy_path_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package kube_e2e - -import ( - "time" - - . "github.com/onsi/ginkgo" -) - -var _ = Describe("Happy Path running on Kubernetes", func() { - Context("creating kube upstream and a vService with a single route to it", func() { - It("should configure envoy with a 200 OK route (backed by helloservice)", func() { - curlEventuallyShouldRespond(curlOpts{ - port: 9090, - method: "POST", - path: "/starwars/query", - body: `{"query": "{hero{name}}"}`, - }, `{"data":{"hero":{"name":"R2-D2"}}}`, time.Second*50) - }) - }) -}) diff --git a/test/kube_e2e/kubernetes_suite_test.go b/test/kube_e2e/kubernetes_suite_test.go deleted file mode 100644 index f807bcb..0000000 --- a/test/kube_e2e/kubernetes_suite_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package kube_e2e - -import ( - "strings" - - . "github.com/onsi/ginkgo" - "github.com/solo-io/gloo/pkg/log" - "github.com/solo-io/gloo/test/helpers" - - "os" - "testing" -) - -const maxLogLines = 250 - -func TestKubernetes(t *testing.T) { - if os.Getenv("RUN_KUBE_TESTS") != "1" { - log.Printf("This test creates kubernetes resources and is disabled by default. To enable, set RUN_KUBE_TESTS=1 in your env.") - return - } - - helpers.RegisterPreFailHandler(func() { - var logs string - for _, component := range []string{"control-plane", "ingress"} { - l, err := helpers.KubectlOut("logs", "-l", "gloo="+component) - - split := strings.Split(l, "\n") - if len(split) > maxLogLines { - l = strings.Join(split[len(split)-maxLogLines:], "\n") - } - - logs += l + "\n" - if err != nil { - logs += "error getting logs for " + component + ": " + err.Error() - } - } - - log.Printf("\n****************************************" + - "\nLOGS FROM THE KUBE BOYS: \n\n" + logs + "\n************************************") - }) - - helpers.RegisterCommonFailHandlers() - log.DefaultOut = GinkgoWriter - RunSpecs(t, "Kubernetes Suite") -} diff --git a/test/helpers.go b/test/testdata/helpers.go similarity index 96% rename from test/helpers.go rename to test/testdata/helpers.go index 5df8520..13d2310 100644 --- a/test/helpers.go +++ b/test/testdata/helpers.go @@ -1,13 +1,13 @@ -package test +package testdata import ( "time" "github.com/fatih/structs" "github.com/pkg/errors" - "github.com/solo-io/sqoop/examples/starwars/imported/starwars" - "github.com/solo-io/sqoop/pkg/dynamic" - "github.com/solo-io/sqoop/pkg/exec" + "github.com/solo-io/sqoop/pkg/engine/dynamic" + "github.com/solo-io/sqoop/pkg/engine/exec" + "github.com/solo-io/sqoop/test/testdata/starwars" "github.com/vektah/gqlgen/neelance/common" "github.com/vektah/gqlgen/neelance/schema" ) diff --git a/examples/starwars/imported/starwars/generated.go b/test/testdata/starwars/generated.go similarity index 100% rename from examples/starwars/imported/starwars/generated.go rename to test/testdata/starwars/generated.go diff --git a/examples/starwars/imported/starwars/model.go b/test/testdata/starwars/model.go similarity index 100% rename from examples/starwars/imported/starwars/model.go rename to test/testdata/starwars/model.go diff --git a/examples/starwars/imported/starwars/models_gen.go b/test/testdata/starwars/models_gen.go similarity index 100% rename from examples/starwars/imported/starwars/models_gen.go rename to test/testdata/starwars/models_gen.go diff --git a/examples/starwars/imported/starwars/resolvers.go b/test/testdata/starwars/resolvers.go similarity index 100% rename from examples/starwars/imported/starwars/resolvers.go rename to test/testdata/starwars/resolvers.go diff --git a/test/utils.go b/test/testdata/utils.go similarity index 64% rename from test/utils.go rename to test/testdata/utils.go index 7f89fcd..738c35b 100644 --- a/test/utils.go +++ b/test/testdata/utils.go @@ -1,80 +1,125 @@ -package test +package testdata import ( - "github.com/solo-io/sqoop/pkg/api/types/v1" - "github.com/solo-io/sqoop/pkg/exec" - "github.com/solo-io/sqoop/pkg/resolvers" - "github.com/solo-io/sqoop/pkg/util" + glooV1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1" + "github.com/solo-io/gloo/projects/gloo/pkg/api/v1/plugins/rest" + "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" + "github.com/solo-io/sqoop/pkg/api/v1" + "github.com/solo-io/sqoop/pkg/engine/exec" + "github.com/solo-io/sqoop/pkg/engine/resolvers" + "github.com/solo-io/sqoop/pkg/translator" "github.com/vektah/gqlgen/graphql" "github.com/vektah/gqlgen/neelance/schema" ) const resolversName = "starwars-resolvers" +const namespace = "default" + +var resolverMetadata = core.Metadata{ + Name: resolversName, + Namespace: namespace, +} var StarWarsSchema = schema.MustParse(starWarsSchemaString) +var x rest.DestinationSpec func StarWarsResolverMap() *v1.ResolverMap { - resolverMap := util.GenerateResolverMapSkeleton(resolversName, StarWarsSchema) - resolverMap.Types["Query"].Fields["hero"].Resolver = &v1.Resolver_GlooResolver{ + resolverMap := translator.GenerateResolverMapSkeleton(resolverMetadata, StarWarsSchema) + resolverMap.Types["Query"].Fields["hero"].Resolver = &v1.FieldResolver_GlooResolver{ GlooResolver: &v1.GlooResolver{ - Function: &v1.GlooResolver_SingleFunction{ - SingleFunction: &v1.Function{ - Upstream: "starwars-rest", - Function: "GetHero", + Action: &glooV1.RouteAction{ + Destination: &glooV1.RouteAction_Single{ + Single: &glooV1.Destination{ + Upstream: core.ResourceRef{ + Name: "starwars-rest", + }, + DestinationSpec: &glooV1.DestinationSpec{ + DestinationType: &glooV1.DestinationSpec_Rest{ + Rest: &rest.DestinationSpec{ + FunctionName: "GetHero", + }, + }, + }, + }, }, }, }, } - resolverMap.Types["Query"].Fields["human"].Resolver = &v1.Resolver_GlooResolver{ + resolverMap.Types["Query"].Fields["human"].Resolver = &v1.FieldResolver_GlooResolver{ GlooResolver: &v1.GlooResolver{ - RequestTemplate: `{"id": {{ index .Args "id" }}}`, - Function: &v1.GlooResolver_SingleFunction{ - SingleFunction: &v1.Function{ - Upstream: "starwars-rest", - Function: "GetCharacter", + RequestTemplate: &v1.RequestTemplate{ + Body: `{"id": {{ index .Args "id" }}}`, + }, + Action: &glooV1.RouteAction{ + Destination: &glooV1.RouteAction_Single{ + Single: &glooV1.Destination{ + Upstream: core.ResourceRef{ + Name: "starwars-rest", + }, + DestinationSpec: &glooV1.DestinationSpec{ + DestinationType: &glooV1.DestinationSpec_Rest{ + Rest: &rest.DestinationSpec{ + FunctionName: "GetCharacter", + }, + }, + }, + }, }, }, }, } - resolverMap.Types["Query"].Fields["droid"].Resolver = &v1.Resolver_GlooResolver{ + resolverMap.Types["Query"].Fields["droid"].Resolver = &v1.FieldResolver_GlooResolver{ GlooResolver: &v1.GlooResolver{ - RequestTemplate: `{"id": {{ index .Args "id" }}}`, - Function: &v1.GlooResolver_SingleFunction{ - SingleFunction: &v1.Function{ - Upstream: "starwars-rest", - Function: "GetCharacter", + RequestTemplate: &v1.RequestTemplate{ + Body: `{"id": {{ index .Args "id" }}}`, + }, + Action: &glooV1.RouteAction{ + Destination: &glooV1.RouteAction_Single{ + Single: &glooV1.Destination{ + Upstream: core.ResourceRef{ + Name: "starwars-rest", + }, + DestinationSpec: &glooV1.DestinationSpec{ + DestinationType: &glooV1.DestinationSpec_Rest{ + Rest: &rest.DestinationSpec{ + FunctionName: "GetCharacter", + }, + }, + }, + }, }, }, }, } - resolverMap.Types["Human"].Fields["friends"].Resolver = &v1.Resolver_GlooResolver{ + resolverMap.Types["Droid"].Fields["friends"].Resolver = &v1.FieldResolver_GlooResolver{ GlooResolver: &v1.GlooResolver{ - RequestTemplate: `{{ marshal (index .Parent "friend_ids") }}`, - Function: &v1.GlooResolver_SingleFunction{ - SingleFunction: &v1.Function{ - Upstream: "starwars-rest", - Function: "GetCharacters", + RequestTemplate: &v1.RequestTemplate{ + Body: `{{ marshal (index .Parent "friend_ids") }}`, + }, + Action: &glooV1.RouteAction{ + Destination: &glooV1.RouteAction_Single{ + Single: &glooV1.Destination{ + Upstream: core.ResourceRef{ + Name: "starwars-rest", + }, + DestinationSpec: &glooV1.DestinationSpec{ + DestinationType: &glooV1.DestinationSpec_Rest{ + Rest: &rest.DestinationSpec{ + FunctionName: "GetCharacters", + }, + }, + }, + }, }, }, }, } - resolverMap.Types["Human"].Fields["appearsIn"].Resolver = &v1.Resolver_TemplateResolver{ + resolverMap.Types["Human"].Fields["appearsIn"].Resolver = &v1.FieldResolver_TemplateResolver{ TemplateResolver: &v1.TemplateResolver{ InlineTemplate: `{{ index .Parent "appears_in" }}}`, }, } - resolverMap.Types["Droid"].Fields["friends"].Resolver = &v1.Resolver_GlooResolver{ - GlooResolver: &v1.GlooResolver{ - RequestTemplate: `{{ marshal (index .Parent "friend_ids") }}`, - Function: &v1.GlooResolver_SingleFunction{ - SingleFunction: &v1.Function{ - Upstream: "starwars-rest", - Function: "GetCharacters", - }, - }, - }, - } - resolverMap.Types["Droid"].Fields["appearsIn"].Resolver = &v1.Resolver_TemplateResolver{ + resolverMap.Types["Droid"].Fields["appearsIn"].Resolver = &v1.FieldResolver_TemplateResolver{ TemplateResolver: &v1.TemplateResolver{ InlineTemplate: `{{ index .Parent "appears_in" }}}`, }, @@ -84,8 +129,7 @@ func StarWarsResolverMap() *v1.ResolverMap { func StarWarsV1Schema() *v1.Schema { return &v1.Schema{ - Name: "starwars-schema", - ResolverMap: resolversName, + Metadata: resolverMetadata, InlineSchema: starWarsSchemaString, } } diff --git a/pkg/exec/executable_resolvers_test.go b/test/unit/executable_resolvers_test.go similarity index 70% rename from pkg/exec/executable_resolvers_test.go rename to test/unit/executable_resolvers_test.go index cc26743..050d164 100644 --- a/pkg/exec/executable_resolvers_test.go +++ b/test/unit/executable_resolvers_test.go @@ -1,4 +1,5 @@ -package exec_test +package unit_test + import ( . "github.com/onsi/ginkgo" @@ -12,16 +13,16 @@ import ( "strings" "github.com/gorilla/mux" - . "github.com/solo-io/sqoop/pkg/exec" - "github.com/solo-io/sqoop/pkg/resolvers" - "github.com/solo-io/sqoop/test" + . "github.com/solo-io/sqoop/pkg/engine/exec" + "github.com/solo-io/sqoop/pkg/engine/resolvers" + "github.com/solo-io/sqoop/test/testdata" ) var _ = Describe("ExecutableResolverMap", func() { var ( proxyAddr string server *httptest.Server - response, _ = json.Marshal(test.LukeSkywalker) + response, _ = json.Marshal(testdata.LukeSkywalker) requestBody *bytes.Buffer createResolver func(typeName, fieldName string) (RawResolver, error) ) @@ -32,23 +33,27 @@ var _ = Describe("ExecutableResolverMap", func() { io.Copy(requestBody, r.Body) w.Write(response) }) + m.HandleFunc("/default.starwars-resolvers.Query.hero", func(w http.ResponseWriter, r *http.Request) { + io.Copy(requestBody, r.Body) + w.Write(response) + }) server = httptest.NewServer(m) proxyAddr = strings.TrimPrefix(server.URL, "http://") - resolverFactory := resolvers.NewResolverFactory(proxyAddr, test.StarWarsResolverMap()) + resolverFactory := resolvers.NewResolverFactory(proxyAddr, testdata.StarWarsResolverMap()) createResolver = resolverFactory.CreateResolver }) AfterEach(func() { server.Close() }) It("does the happy path", func() { - execResolve, err := NewExecutableResolvers(test.StarWarsSchema, createResolver) + execResolve, err := NewExecutableResolvers(testdata.StarWarsSchema, createResolver) Expect(err).NotTo(HaveOccurred()) - res, err := execResolve.Resolve(test.StarWarsSchema.Types["Query"], "hero", Params{}) + res, err := execResolve.Resolve(testdata.StarWarsSchema.Types["Query"], "hero", Params{}) Expect(err).NotTo(HaveOccurred()) data, ok := res.GoValue().(map[string]interface{}) Expect(ok).To(BeTrue()) - m, _ := toMap(test.LukeSkywalker) + m, _ := toMap(testdata.LukeSkywalker) // all the keys match for k, v := range m { Expect(data).To(HaveKey(k)) diff --git a/pkg/resolvers/gloo/gloo_resolvers_test.go b/test/unit/gloo_resolvers_test.go similarity index 56% rename from pkg/resolvers/gloo/gloo_resolvers_test.go rename to test/unit/gloo_resolvers_test.go index 113fc73..a0e74f3 100644 --- a/pkg/resolvers/gloo/gloo_resolvers_test.go +++ b/test/unit/gloo_resolvers_test.go @@ -1,33 +1,35 @@ -package gloo_test +package unit_test import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" "bytes" + "encoding/json" "io" "net/http" "net/http/httptest" "strings" "github.com/gorilla/mux" - "github.com/solo-io/sqoop/pkg/api/types/v1" - . "github.com/solo-io/sqoop/pkg/resolvers/gloo" - "github.com/solo-io/sqoop/test" + "github.com/solo-io/sqoop/pkg/api/v1" + . "github.com/solo-io/sqoop/pkg/engine/resolvers/gloo" + "github.com/solo-io/sqoop/test/testdata" ) var _ = Describe("GlooResolvers", func() { var ( mockProxyAddr string server *httptest.Server - response = []byte(`{"have":"a","nice":"day","okay":"?"}`) + response, _ = json.Marshal(testdata.LukeSkywalker) resolverFactory *ResolverFactory requestBody *bytes.Buffer ) BeforeEach(func() { requestBody = &bytes.Buffer{} m := mux.NewRouter() - m.HandleFunc("/mytype.myfield", func(w http.ResponseWriter, r *http.Request) { + m.HandleFunc("/default.mymap.mytype.myfield", func(w http.ResponseWriter, r *http.Request) { io.Copy(requestBody, r.Body) w.Write(response) }) @@ -43,14 +45,22 @@ var _ = Describe("GlooResolvers", func() { typeName := "mytype" fieldName := "myfield" gResolver := &v1.GlooResolver{ - RequestTemplate: `REQUEST: best scene: {{ marshal (index .Args "best_scene") }} friendIds: {{ marshal (index .Parent "CharacterFields") }}`, - ResponseTemplate: `RESPONSE: {{ marshal (index .Result "nice") }}`, + RequestTemplate: &v1.RequestTemplate{ + Body: `REQUEST: best scene: {{ marshal (index .Args "best_scene") }} friendIds: {{ marshal (index .Parent "CharacterFields") }}`, + }, + ResponseTemplate: &v1.ResponseTemplate{ + Body: `RESPONSE: {{ marshal (index . "mass") }}`, + }, + } + resolverMetadata := core.ResourceRef{ + Name: "mymap", + Namespace: "default", } Context("it returns a resolver which ", func() { It("renders the template as the request body", func() { - rawResolver, err := resolverFactory.CreateResolver(typeName, fieldName, gResolver) + rawResolver, err := resolverFactory.CreateResolver(resolverMetadata, typeName, fieldName, gResolver) Expect(err).NotTo(HaveOccurred()) - _, err = rawResolver(test.LukeSkywalkerParams) + _, err = rawResolver(testdata.LukeSkywalkerParams) Expect(err).NotTo(HaveOccurred()) str := requestBody.String() Expect(str).To(Equal(`REQUEST: best scene: "cloud city" friendIds: ` + @@ -58,11 +68,11 @@ var _ = Describe("GlooResolvers", func() { `"ID":"1000","Name":"Luke Skywalker","TypeName":"Human"}`)) }) It("renders the result template on the json response body", func() { - rawResolver, err := resolverFactory.CreateResolver(typeName, fieldName, gResolver) + rawResolver, err := resolverFactory.CreateResolver(resolverMetadata, typeName, fieldName, gResolver) Expect(err).NotTo(HaveOccurred()) - b, err := rawResolver(test.LukeSkywalkerParams) + b, err := rawResolver(testdata.LukeSkywalkerParams) Expect(err).NotTo(HaveOccurred()) - Expect(b).To(Equal([]byte(`RESPONSE: "day"`))) + Expect(b).To(Equal([]byte(`RESPONSE: 77`))) }) }) }) diff --git a/pkg/dynamic/ordered_map_test.go b/test/unit/ordered_map_test.go similarity index 69% rename from pkg/dynamic/ordered_map_test.go rename to test/unit/ordered_map_test.go index 3ffadf2..141baae 100644 --- a/pkg/dynamic/ordered_map_test.go +++ b/test/unit/ordered_map_test.go @@ -1,31 +1,31 @@ -package dynamic_test +package unit_test import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - . "github.com/solo-io/sqoop/pkg/dynamic" + "github.com/solo-io/sqoop/pkg/engine/dynamic" "github.com/vektah/gqlgen/neelance/schema" ) var _ = Describe("Values", func() { Describe("OrderedMap", func() { - var om *OrderedMap + var om *dynamic.OrderedMap BeforeEach(func() { - om = NewOrderedMap() + om = dynamic.NewOrderedMap() }) It("gets by key", func() { val := om.Get("foo") Expect(val).To(BeNil()) om.Keys = append(om.Keys, "foo") - expected := &String{Scalar: &schema.Scalar{Name: "String"}, Data: "bar"} + expected := &dynamic.String{Scalar: &schema.Scalar{Name: "String"}, Data: "bar"} om.Values = append(om.Values, expected) val = om.Get("foo") Expect(val).To(Equal(expected)) }) It("deletes by key", func() { om.Keys = append(om.Keys, "foo") - expected := &String{Scalar: &schema.Scalar{Name: "String"}, Data: "bar"} + expected := &dynamic.String{Scalar: &schema.Scalar{Name: "String"}, Data: "bar"} om.Values = append(om.Values, expected) val := om.Get("foo") Expect(val).To(Equal(expected)) @@ -36,7 +36,7 @@ var _ = Describe("Values", func() { It("inserts key/val", func() { val := om.Get("foo") Expect(val).To(BeNil()) - expected := &String{Scalar: &schema.Scalar{Name: "String"}, Data: "bar"} + expected := &dynamic.String{Scalar: &schema.Scalar{Name: "String"}, Data: "bar"} om.Set("foo", expected) val = om.Get("foo") Expect(val).To(Equal(expected)) diff --git a/pkg/graphql/router_test.go b/test/unit/router_test.go similarity index 68% rename from pkg/graphql/router_test.go rename to test/unit/router_test.go index b22de44..55a042f 100644 --- a/pkg/graphql/router_test.go +++ b/test/unit/router_test.go @@ -1,46 +1,46 @@ -package graphql_test +package unit_test import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/solo-io/sqoop/pkg/engine/router" "bytes" "io/ioutil" "net/http" "net/http/httptest" - . "github.com/solo-io/sqoop/pkg/graphql" - "github.com/solo-io/sqoop/test" + "github.com/solo-io/sqoop/test/testdata" ) var _ = Describe("Router", func() { var ( - router *Router - server *httptest.Server + localRouter *router.Router + server *httptest.Server ) BeforeEach(func() { - router = NewRouter() - server = httptest.NewServer(router) + localRouter = router.NewRouter() + server = httptest.NewServer(localRouter) }) AfterEach(func() { server.Close() }) It("serves and updates routes dynamically from graphql endpoints", func() { - testEndpoints := []*Endpoint{ + testEndpoints := []*router.Endpoint{ { SchemaName: "StarWars1", RootPath: "/root1", QueryPath: "/query2", - ExecSchema: test.StarWarsExecutableSchema("no-address-defined"), + ExecSchema: testdata.StarWarsExecutableSchema("no-address-defined"), }, { SchemaName: "StarWars2", RootPath: "/root2", QueryPath: "/query2", - ExecSchema: test.StarWarsExecutableSchema("no-address-defined"), + ExecSchema: testdata.StarWarsExecutableSchema("no-address-defined"), }, } - router.UpdateEndpoints(testEndpoints...) + localRouter.UpdateEndpoints(testEndpoints) for _, ep := range testEndpoints { res, err := http.Get(server.URL + ep.RootPath) Expect(err).NotTo(HaveOccurred()) @@ -54,7 +54,7 @@ var _ = Describe("Router", func() { Expect(err).NotTo(HaveOccurred()) Expect(string(data)).To(ContainSubstring(`{"data":{"hero":null},"errors":` + `[{"message":"executing resolver for field \"hero\": failed executing resolver for Query.hero: ` + - `performing http post: Post http://no-address-defined/Query.hero: dial tcp: lookup no-address-defined on`)) + `performing http post: Post http://no-address-defined/default.starwars-resolvers.Query.hero: dial tcp`)) } }) }) diff --git a/pkg/dynamic/dynamic_suite_test.go b/test/unit/sqoop_unit_suite_test.go similarity index 73% rename from pkg/dynamic/dynamic_suite_test.go rename to test/unit/sqoop_unit_suite_test.go index 10aafa4..95cab96 100644 --- a/pkg/dynamic/dynamic_suite_test.go +++ b/test/unit/sqoop_unit_suite_test.go @@ -1,4 +1,4 @@ -package dynamic_test +package unit_test import ( "testing" @@ -9,5 +9,5 @@ import ( func TestDynamic(t *testing.T) { RegisterFailHandler(Fail) - RunSpecs(t, "Dynamic Suite") + RunSpecs(t, "Sqoop unit Suite") } diff --git a/pkg/resolvers/template/template_resolvers_test.go b/test/unit/template_resolvers_test.go similarity index 69% rename from pkg/resolvers/template/template_resolvers_test.go rename to test/unit/template_resolvers_test.go index 249bd6a..c4d715a 100644 --- a/pkg/resolvers/template/template_resolvers_test.go +++ b/test/unit/template_resolvers_test.go @@ -1,23 +1,20 @@ -package template_test +package unit_test import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/solo-io/sqoop/pkg/api/types/v1" - . "github.com/solo-io/sqoop/pkg/resolvers/template" - "github.com/solo-io/sqoop/test" + "github.com/solo-io/sqoop/pkg/engine/resolvers/template" + "github.com/solo-io/sqoop/test/testdata" ) var _ = Describe("TemplateResolvers", func() { Context("happy path with simple template and params", func() { - tResolver := &v1.TemplateResolver{ - InlineTemplate: "{{ marshal . }}", - } + tResolver := "{{ marshal . }}" It("returns a resolver which renders the template", func() { - rawResolver, err := NewTemplateResolver(tResolver) + rawResolver, err := template.NewTemplateResolver(tResolver) Expect(err).NotTo(HaveOccurred()) - b, err := rawResolver(test.LukeSkywalkerParams) + b, err := rawResolver(testdata.LukeSkywalkerParams) Expect(err).NotTo(HaveOccurred()) Expect(b).To(Equal([]byte(`{"Args":{"acting":5,"best_scene":"cloud city"},` + `"Parent":{"CharacterFields":{"AppearsIn":["NEWHOPE","EMPIRE","JEDI"],` + diff --git a/version b/version deleted file mode 100644 index 17e51c3..0000000 --- a/version +++ /dev/null @@ -1 +0,0 @@ -0.1.1 diff --git a/version/check.go b/version/check.go new file mode 100644 index 0000000..3c284ae --- /dev/null +++ b/version/check.go @@ -0,0 +1,46 @@ +package version + +import ( + "github.com/pkg/errors" + version "github.com/solo-io/go-utils/versionutils" + "github.com/solo-io/solo-kit/pkg/utils/log" +) + +func CheckVersions() error { + log.Printf("Checking expected solo kit and gloo versions...") + tomlTree, err := version.ParseToml() + if err != nil { + return err + } + + expectedGlooVersion, err := version.GetVersion(version.GlooPkg, tomlTree) + if err != nil { + return err + } + + expectedSoloKitVersion, err := version.GetVersion(version.SoloKitPkg, tomlTree) + if err != nil { + return err + } + + log.Printf("Checking repo versions...") + actualGlooVersion, err := version.GetGitVersion("../gloo") + if err != nil { + return err + } + expectedTaggedGlooVersion := version.GetTag(expectedGlooVersion) + if expectedTaggedGlooVersion != actualGlooVersion { + return errors.Errorf("Expected gloo version %s, found gloo version %s in repo. Run 'make pin-repos' or fix manually.", expectedTaggedGlooVersion, actualGlooVersion) + } + + actualSoloKitVersion, err := version.GetGitVersion("../solo-kit") + if err != nil { + return err + } + expectedTaggedSoloKitVersion := version.GetTag(expectedSoloKitVersion) + if expectedTaggedSoloKitVersion != actualSoloKitVersion { + return errors.Errorf("Expected solo kit version %s, found solo kit version %s in repo. Run 'make pin-repos' or fix manually.", expectedTaggedSoloKitVersion, actualSoloKitVersion) + } + log.Printf("Versions are pinned correctly.") + return nil +} diff --git a/version/linked.go b/version/linked.go new file mode 100644 index 0000000..94322b2 --- /dev/null +++ b/version/linked.go @@ -0,0 +1,10 @@ +package version + +var UndefinedVersion = "undefined" +var DevVersion = "dev" // default version set if running "make glooctl" +// This will be set by the linker during build +var Version = UndefinedVersion + +func IsReleaseVersion() bool { + return Version != UndefinedVersion && Version != DevVersion +}